package com.tiancheng.trade.order.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tiancheng.trade.commom.core.data.PageInfo;
import com.tiancheng.trade.commom.core.data.Result;
import com.tiancheng.trade.commom.core.data.notify.PayResultMsgBO;
import com.tiancheng.trade.commom.core.data.notify.PaySubFlowResultMsgBO;
import com.tiancheng.trade.commom.core.enums.PaySceneEnum;
import com.tiancheng.trade.commom.core.enums.PaymentChannelEnum;
import com.tiancheng.trade.commom.core.enums.PaymentWayEnum;
import com.tiancheng.trade.commom.core.exception.BaseException;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.commom.web.feign.MerchantFeignService;
import com.tiancheng.trade.commom.web.model.response.MerchantPaymentChannelBO;
import com.tiancheng.trade.commom.web.model.response.PaymentPayWayQueryResponse;
import com.tiancheng.trade.commom.web.model.response.UnifiedOrderResVO;
import com.tiancheng.trade.commom.core.utils.DateTimeUtil;
import com.tiancheng.trade.order.bo.notify.PayResultHandlerResultBO;
import com.tiancheng.trade.order.bo.notify.PayResultOrderBO;
import com.tiancheng.trade.order.bo.notify.PayResultSubOrderBO;
import com.tiancheng.trade.order.util.SecurityUtils;
import com.tiancheng.trade.order.bo.*;
import com.tiancheng.trade.order.bo.cmq.ScrmOrder;
import com.tiancheng.trade.order.bo.cmq.ScrmSubOrders;
import com.tiancheng.trade.order.bo.cmq.VipMsgBO;
import com.tiancheng.trade.order.enums.*;
import com.tiancheng.trade.order.feign.DeductionPayCheckService;
import com.tiancheng.trade.order.feign.OrderSubSystemService;
import com.tiancheng.trade.commom.web.model.request.CloseOrderDTO;
import com.tiancheng.trade.order.feign.dto.DeductionPayCheckDTO;
import com.tiancheng.trade.commom.web.model.request.OrderPayDTO;
import com.tiancheng.trade.order.feign.dto.orderSubSystem.OrderQueryParamDTO;
import com.tiancheng.trade.order.mapper.OrderMapper;
import com.tiancheng.trade.order.model.*;
import com.tiancheng.trade.order.model.join.OrderJoin;
import com.tiancheng.trade.order.service.*;
import com.tiancheng.trade.order.util.*;
import com.tiancheng.trade.order.util.cache.OrderCache;
import com.tiancheng.trade.order.util.cmq.queue.producer.VipMessageProducer;
import com.tiancheng.trade.order.util.lock.Lock;
import com.tiancheng.trade.order.vo.admin.QueryOrdersVO;
import com.tiancheng.trade.order.vo.admin.QueryTotalOrderVO;
import com.tiancheng.trade.order.vo.admin.res.*;
import com.tiancheng.trade.order.vo.api.CloseOrderVO;
import com.tiancheng.trade.order.vo.api.OrderVO;
import com.tiancheng.trade.order.vo.api.ScrmOrderQueryVO;
import com.tiancheng.trade.order.vo.api.UpdateOrderVO;
import com.tiancheng.trade.order.vo.api.res.CreateOrderResVO;
import com.tiancheng.trade.order.vo.api.res.OrderDiscountInfoVO;
import com.tiancheng.trade.order.vo.app.CashierVO;
import com.tiancheng.trade.order.vo.app.OrderCenterQueryVO;
import com.tiancheng.trade.order.vo.app.OrderQueryVO;
import com.tiancheng.trade.order.vo.app.res.OrderPrePayResVO;
import com.tiancheng.trade.order.vo.app.res.OrderQueryResVO;
import com.tiancheng.trade.order.vo.open.order.OpenCashierResVO;
import com.tiancheng.trade.order.vo.open.order.OpenOrderPayVO;
import com.tiancheng.trade.order.vo.parallel.AppleInAppPayVO;
import com.tiancheng.trade.order.vo.parallel.res.AppleInAppPayResVO;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 项目名称：trade-order
 * 类 名 称：OrderService
 * 类 描 述：主订单操作
 * 创建时间：2024-02-21 13:49
 * 创 建 人：liudong
 */
@Slf4j
@Service
public class OrderServiceImpl extends BaseOrderPayService implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private CouponService couponService;
    @Resource
    private RefundOrdersDetailService refundOrdersDetailService;
    @Resource
    private MerchantFeignService merchantFeignService;
    @Resource
    private OrderSubSystemService orderSubSystemService;
    @Resource
    private CreditOrderService creditOrderService;
    @Resource
    private TallyService tallyService;
    @Resource
    private VipEquityService vipEquityService;
    @Resource
    private VipMessageProducer vipMessageProducer;
    @Resource
    private DeductionPayCheckService deductionPayCheckService;
    @Resource
    private OrderBatchService orderBatchService;


    @Override
    public OrderDetailResVO queryOrderDetailsByOrderId(String OrderId) throws BusinessException {
        OrderDetailResVO parametersVO = new OrderDetailResVO();
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("order_id", OrderId);
        Order order = orderMapper.selectOne(conditions);
        OrderDetailVO orderDetailVO = ObjectConvertUtil.convert(order, OrderDetailVO.class).get();
        orderDetailVO.setPayExpiryTime(orderDetailVO.getPayExpiryTime());
        orderDetailVO.setPayChannelCN(PaymentChannelEnum.getDesc(order.getPayChannel()));
        orderDetailVO.setPayWayCN(PaymentWayEnum.getDesc(order.getPayWay()));
        List<SubOrder> subOrders = null;
        List<SubOrderDetailVO> subOrderDetailVO = null;
        List<RefundOrdersDetail> refundOrdersDetails = null;
        List<RefundDetailResVO> refundDetailResVOS = null;
        List<Coupon> coupons = null;
        List<CouponDetailVO> couponDetailVOS = null;
        if (null != order) {
            subOrders = subOrderService.findByOrderId(order.getOrderId());
            subOrderDetailVO = subOrders.stream().map(subOrder -> ObjectConvertUtil.convert(subOrder, SubOrderDetailVO.class).get()).collect(Collectors.toList());
            subOrderDetailVO.forEach(subOrderDetailVO1 -> subOrderDetailVO1.setSubOrderStatusCN(SubOrderStatusEnum.getDesc(subOrderDetailVO1.getSubOrderStatus())));

            refundOrdersDetails = refundOrdersDetailService.getRefundList(order.getOrderId());
            if (CollectionUtils.isNotEmpty(refundOrdersDetails)) {
                refundDetailResVOS = refundOrdersDetails.stream().map(refund -> ObjectConvertUtil.convert(refund, RefundDetailResVO.class).get()).collect(Collectors.toList());
            }
            coupons = couponService.getCouponList(order.getOrderId());
            orderDetailVO.setCouponNum(coupons.size());
            orderDetailVO.setSettlementTags(order.getSettlementTags());
            orderDetailVO.setOrderStatusCN(OrderStatusEnum.getDesc(orderDetailVO.getOrderStatus()));
            orderDetailVO.setPayStatusCN(OrderPayStatusEnum.getDesc(orderDetailVO.getPayStatus()));
            orderDetailVO.setOrderSourceCN(OrderSourceEnum.getDesc(orderDetailVO.getOrderSource()));
            orderDetailVO.setOrderTypeCN(OrderTypeEnum.getDesc(orderDetailVO.getOrderType()));
        }
        if (CollectionUtils.isNotEmpty(subOrderDetailVO)) {
            orderDetailVO.setMerchantCode(subOrderDetailVO.get(0).getMerchantCode());
            orderDetailVO.setMerchantName(subOrderDetailVO.get(0).getMerchantName());
        }
        if (CollectionUtils.isNotEmpty(coupons)) {
            couponDetailVOS = coupons.stream().map(coupon -> ObjectConvertUtil.convert(coupon, CouponDetailVO.class).get()).collect(Collectors.toList());
            couponDetailVOS.stream().forEach(a -> {
                a.setIsCoupon(true);
                a.setCouponTypeCN(CouponTypeEnum.getDesc(a.getCouponType()));
                a.setDiscountSourceCN(OrderSourceEnum.getDesc(a.getDiscountSource()));
            });
            orderDetailVO.setCouponNum(coupons.size());
            orderDetailVO.setIsCoupon(true);
        }
        final List<VipEquity> vipEquities = vipEquityService.findByOrderId(Collections.singletonList(order.getOrderId()));
        final VipEquityResVo vipEquityResVo = new VipEquityResVo();
        if (CollectionUtils.isNotEmpty(vipEquities)) {
            vipEquityResVo.setDiscount(vipEquities.get(0).getDiscount());
            vipEquityResVo.setDiscountAmount(0);
            vipEquityResVo.setUseBeam(0);
            vipEquityResVo.setUseBeamAmount(0);
            vipEquityResVo.setVipLevel(vipEquities.get(0).getVipLevel());
            vipEquities.forEach(v -> {
                vipEquityResVo.setDiscountAmount(vipEquityResVo.getDiscountAmount() + v.getDiscountAmount());
                vipEquityResVo.setUseBeam(vipEquityResVo.getUseBeam() + v.getUseBeam());
                vipEquityResVo.setUseBeamAmount(vipEquityResVo.getUseBeamAmount() + v.getUseBeamAmount());
            });
            vipEquityResVo.setVipTotalAmount(vipEquityResVo.getDiscountAmount() + vipEquityResVo.getUseBeamAmount());
        }
        parametersVO.setVipInfo(vipEquityResVo);
        parametersVO.setOrderDetailVO(orderDetailVO);
        parametersVO.setSubOrders(subOrderDetailVO);
        parametersVO.setCoupons(couponDetailVOS);
        parametersVO.setRefunds(refundDetailResVOS);
        return parametersVO;
    }

    @Override
    public PageInfo<OrderResVO> queryOrderList(QueryOrdersVO orderVO) {
        QueryWrapper<Order> query = buildQuery(orderVO);
        query.orderByDesc("created_dt");
        PageInfo<Order> pageInfo = orderMapper.find(query, orderVO.getCurrentPage(), orderVO.getPageSize());
        PageInfo<OrderResVO> pageRes = new PageInfo<>();
        List<OrderResVO> vos = pageInfo.getList().stream().map(order -> ObjectConvertUtil.convert(order, OrderResVO.class).get()).collect(Collectors.toList());
        vos.forEach(orderResVO -> orderResVO.setOrderStatusCN(OrderStatusEnum.getDesc(orderResVO.getOrderStatus())));
        vos.forEach(orderResVO -> orderResVO.setPayStatusCN(OrderPayStatusEnum.getDesc(orderResVO.getPayStatus())));
        vos.forEach(orderResVO -> orderResVO.setOrderSource(OrderSourceEnum.getDesc(orderResVO.getOrderSource())));
        pageRes.setList(vos);
        pageRes.setPagination(pageInfo.getPagination());
        return pageRes;
    }

    private QueryWrapper<Order> buildQuery(QueryOrdersVO orderVO) {
        QueryWrapper<Order> query = new QueryWrapper<>();

        if (StrUtil.isNotBlank(orderVO.getOrderId())) {
            query.eq("order_id", orderVO.getOrderId());
        }
        if (StrUtil.isNotBlank(orderVO.getOriginalOrderId())) {
            query.eq("original_order_id", orderVO.getOriginalOrderId());
        }
        if (StrUtil.isNotBlank(orderVO.getPaymentId())) {
            query.eq("payment_id", orderVO.getPaymentId());
        }
        if (StrUtil.isNotBlank(orderVO.getFlowId())) {
            query.eq("flow_id", orderVO.getFlowId());
        }
        if (StrUtil.isNotBlank(orderVO.getOrderSource())) {
            query.eq("order_source", orderVO.getOrderSource());
        }
        if (StrUtil.isNotBlank(orderVO.getOrderStatus())) {
            query.eq("order_status", orderVO.getOrderStatus());
        }
        if (StrUtil.isNotBlank(orderVO.getPayStatus())) {
            query.eq("pay_status", orderVO.getPayStatus());
        }
        if (!StringUtils.isEmpty(orderVO.getCreateTimeBegin()) && !StringUtils.isEmpty(orderVO.getCreateTimeEnd())) {
            query.between("created_dt", DateTimeUtil.format(orderVO.getCreateTimeBegin() + " 00:00:00", "yyyy-MM-dd HH:mm:ss"), DateTimeUtil.format(orderVO.getCreateTimeEnd() + " 23:59:59", "yyyy-MM-dd HH:mm:ss"));
        }

        if (!StringUtils.isEmpty(orderVO.getPayTimeBegin()) && !StringUtils.isEmpty(orderVO.getPayTimeEnd())) {
            query.between("pay_time", DateTimeUtil.format(orderVO.getPayTimeBegin() + " 00:00:00", "yyyy-MM-dd HH:mm:ss"), DateTimeUtil.format(orderVO.getPayTimeEnd() + " 23:59:59", "yyyy-MM-dd HH:mm:ss"));
        }

        return query;
    }

    @Override
    public void exportOrderList(QueryOrdersVO vo, HttpServletResponse response) throws IOException {
        QueryWrapper<Order> query = buildQuery(vo);
        query.orderByAsc("created_dt");

        // 默认最多查询一个月的订单
        if (StrUtil.isBlank(vo.getCreateTimeBegin()) || StrUtil.isBlank(vo.getCreateTimeEnd())) {
            DateTime now = new DateTime();
            query.between("created_dt", DateUtil.beginOfMonth(now), DateUtil.endOfMonth(now));
        }
        query.select("order_id", "payment_id", "flow_id", "order_source", "order_name", "order_status", "pay_status", "order_amount", "platform_discount", "pay_amount", "discount", "created_dt", "pay_time", "business_type");
        List<Order> orders = orderMapper.selectList(query);
        if (orders.isEmpty()) throw new BaseException(400, "当前查询条件无数据");

        // 查询优惠券
        Map<String, List<Coupon>> couponMap = couponService.getCouponByOrderId(orders.stream().map(Order::getOrderId).collect(Collectors.toList())).stream().collect(Collectors.groupingBy(Coupon::getOrderId));

        // 定义表头
        List<List<String>> head = new ArrayList<>();
        head.add(Collections.singletonList("序号"));
        head.add(Collections.singletonList("主订单号"));
        head.add(Collections.singletonList("支付订单号"));
        head.add(Collections.singletonList("渠道支付订单号"));
        head.add(Collections.singletonList("业务场景"));
        head.add(Collections.singletonList("订单名称"));
        head.add(Collections.singletonList("订单状态"));
        head.add(Collections.singletonList("支付状态"));
        head.add(Collections.singletonList("应收金额(元)"));
        head.add(Collections.singletonList("实付金额(元)"));
        head.add(Collections.singletonList("优惠券名称"));
        head.add(Collections.singletonList("优惠总金额"));
        head.add(Collections.singletonList("优惠（平台）"));
        head.add(Collections.singletonList("优惠（渠道）"));
        head.add(Collections.singletonList("优惠（会员）"));
        head.add(Collections.singletonList("下单时间"));
        head.add(Collections.singletonList("交易时间"));
        head.add(Collections.singletonList("业务类型"));

        final List<VipEquity> vipEquities = vipEquityService.findByOrderId(orders.stream().map(Order::getOrderId).collect(Collectors.toSet()));

        // 定义数据
        List<List<Object>> data = new ArrayList<>();
        orders.forEach(order -> {
            String orderId = order.getOrderId();
            Coupon coupon = couponMap.getOrDefault(orderId, Collections.emptyList()).stream().findFirst().orElse(new Coupon());
            LocalDateTime createdDt = order.getCreatedDt();
            LocalDateTime payTime = order.getPayTime();
            int vipDiscount = 0;
            if (CollectionUtils.isNotEmpty(vipEquities)) {
                final Optional<VipEquity> first = vipEquities.stream().filter(v -> v.getOrderId().equals(orderId)).findFirst();
                if (first.isPresent()) {
                    vipDiscount = first.get().getDiscountAmount() + first.get().getUseBeamAmount();
                }
            }
            data.add(Arrays.asList(orders.indexOf(order) + 1, orderId, order.getPaymentId(), order.getFlowId(), OrderSourceEnum.getDesc(order.getOrderSource()), order.getOrderName(), OrderStatusEnum.getDesc(order.getOrderStatus()), OrderPayStatusEnum.getDesc(order.getPayStatus()), NumberUtil.div(Optional.ofNullable(order.getOrderAmount()).orElse(0), Integer.valueOf(100)), NumberUtil.div(Optional.ofNullable(order.getPayAmount()).orElse(0), Integer.valueOf(100)), coupon.getCouponName(), NumberUtil.div(Optional.ofNullable(order.getDiscount()).orElse(0), Integer.valueOf(100)), NumberUtil.div(NumberUtil.sub(Optional.ofNullable(order.getPlatformDiscount()).orElse(0), Optional.of(vipDiscount).orElse(0)), 100), NumberUtil.div(NumberUtil.sub(Optional.ofNullable(order.getDiscount()).orElse(0), Optional.ofNullable(order.getPlatformDiscount()).orElse(0)), 100), NumberUtil.div(Optional.of(vipDiscount).orElse(0), Integer.valueOf(100)), createdDt != null ? createdDt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : "无", payTime != null ? payTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) : "无", ""));
        });

        ExcelBuildUtil.easyExcel(head, data, response);
    }

    @Override
    public String getPayChannelAndSetMerchant(List<SubOrderBO> subOrderBOS, OrderVO orderVO) throws BusinessException {
        // 调用商户中心，根据商品，确认支付渠道，并判断禁止跨渠道下单
        Set<String> merchantCods = subOrderBOS.stream().map(SubOrderBO::getMerchantCode).collect(Collectors.toSet());
        //根据商户号获取支付渠道
        List<MerchantPaymentChannelBO> channels = super.getPayChannelMapsByMerchantCodes(merchantCods);
        // 如果没有查询到商户中心商品，就默认cmb
        String payChannel = PaymentChannelEnum.UMS.getCode();
        if (channels.size() > 0) {
            Set<String> channelSet = channels.stream().map(MerchantPaymentChannelBO::getPaymentChannelCode).collect(Collectors.toSet());
            BusinessErrorInfoEnum.ORDER_CREATE_CHANNEL_DIFF.assertIsTrue(channelSet.size() == 1);
            payChannel = channelSet.iterator().next();
            for (SubOrderBO subOrderBO : subOrderBOS) {
                channels.stream().filter(c -> !StringUtils.isEmpty(c.getMerchantCode()) && !StringUtils.isEmpty(c.getMerchantName()) && c.getMerchantCode().equals(subOrderBO.getMerchantCode())).findFirst().ifPresent(p -> {
                    subOrderBO.setMerchantName(p.getMerchantName());
                });
                // 设置商品成本价: 业务侧传则存业务侧的数据，不传则存为商品销售价
                if (subOrderBO.getProductCost() == null || subOrderBO.getProductCost() == 0) {
                    subOrderBO.setProductCost(subOrderBO.getSubOrderAmount());
                }
            }
        }
        return payChannel;
    }

    @Override
    @Transactional
    public String create(OrderBO orderBO, List<SubOrderBO> subOrderBOS, List<CouponBO> couponBOS, VipBO vipBO) throws BusinessException {
        // 重复下单判断
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("order_id", orderBO.getOrderId());
        conditions.eq("uid", orderBO.getUid());
        BusinessErrorInfoEnum.ORDER_CREATE_REPEAT.assertIsTrue(orderMapper.selectCount(conditions) == 0);

        // 如果是续费订单、尾款订单，验证关联的上一笔订单有效性
        OrderTypeEnum orderTypeEnum = EnumUtil.getEnumByCode(OrderTypeEnum.class, orderBO.getOrderType());
        if (orderTypeEnum == OrderTypeEnum.renew || orderTypeEnum == OrderTypeEnum.balance) {
            QueryWrapper<Order> originalConditions = new QueryWrapper<>();
            originalConditions.eq("order_id", orderBO.getOriginalOrderId());
            originalConditions.eq("order_status", OrderStatusEnum.paid.getCode());
            Long count = orderMapper.selectCount(originalConditions);
            BusinessErrorInfoEnum.ORDER_CREATE_ORIGINAL.assertIsTrue(count > 0);
        }

        // 保存主订单
        Order order = ObjectConvertUtil.convert(orderBO, Order.class).get();
        order.setOrderStatus(OrderStatusEnum.waiting_pay.getCode());
        order.setPayStatus(OrderPayStatusEnum.un_pay.getCode());
        order.setSubOrderCnt(subOrderBOS.size());
        order.setCreatedDt(orderBO.getCreateTime());
        order.setBatchId(orderBO.getBatchId());
        order.setRefundTimes(0);        //初始退款次数为0
        // 支付过期时间，下单时间20分钟后
        if (order.getPayExpiryTime() == null) {
            order.setPayExpiryTime(order.getCreatedDt().plusMinutes(20));
        }
        // 计算主订单的优惠金额、实际支付金额
        int discountTotal = 0;//主订单优惠券金额
        if (couponBOS.size() > 0) {
            discountTotal = couponBOS.stream().mapToInt(CouponBO::getDiscount).sum();
            order.setDiscount(discountTotal);//优惠总金额，初始设为平台优惠金额
            order.setPlatformDiscount(discountTotal);//平台优惠金额
            order.setPayAmount(order.getOrderAmount() - order.getPlatformDiscount());//订单支付总金额=订单应收金额-平台优惠总金额
        } else {
            order.setPayAmount(order.getOrderAmount());
        }

        // 保存子订单
        // 计算出子订单的优惠金额（目前只支持平台单张优惠，分摊到各个子订单）
        if (couponBOS.size() > 0) {
            for (int i = 0; i < subOrderBOS.size(); i++) {
                SubOrderBO subOrderBO = subOrderBOS.get(i);
                if (i == subOrderBOS.size() - 1) {
                    subOrderBO.setDiscount(discountTotal - subOrderBOS.stream().mapToInt(SubOrderBO::getDiscount).sum());
                    subOrderBO.setPlatformDiscount(discountTotal - subOrderBOS.stream().mapToInt(SubOrderBO::getPlatformDiscount).sum());
                } else {
                    // 1、计算子订单所占总金额的比例，小数点后取10位，然后截取
                    // 2、总优惠金额*子订单比例，然后四舍五入取整
                    int divide = BigDecimal.valueOf(subOrderBO.getSubOrderAmount()).divide(BigDecimal.valueOf(order.getOrderAmount()), 10, BigDecimal.ROUND_DOWN).multiply(BigDecimal.valueOf(discountTotal)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
                    subOrderBO.setDiscount(divide);
                    subOrderBO.setPlatformDiscount(divide);
                }
                subOrderBO.setIsCoupon(subOrderBO.getDiscount() > 0 ? 1 : 0);
            }
        }

        // 会员权益分摊
        if (vipBO != null) {
            final List<VipEquity> vipEquities = getVipEquity(orderBO, vipBO);
            if (CollectionUtils.isNotEmpty(vipEquities)) {
                final int sumVipDiscount = vipEquities.stream().mapToInt(VipEquity::getDiscountAmount).sum();
                final int sumVipUseBeam = vipEquities.stream().mapToInt(VipEquity::getUseBeamAmount).sum();
                order.setDiscount(order.getDiscount() + sumVipDiscount + sumVipUseBeam);
                order.setPlatformDiscount(order.getDiscount());
                order.setPayAmount(order.getOrderAmount() - order.getPlatformDiscount());
                order.setIsCoupon(order.getDiscount() > 0 ? 1 : 0);
                subOrderBOS.forEach(s -> {
                    final VipEquity vipEquity = vipEquities.stream().filter(v -> s.getOrderId().equals(v.getOrderId()) && v.getSubOrderId().equals(s.getSubOrderId())).findFirst().get();
                    s.setDiscount(s.getDiscount() + vipEquity.getDiscountAmount() + vipEquity.getUseBeamAmount());
                    s.setPlatformDiscount(s.getDiscount());
                });
                vipEquityService.saveBatch(vipEquities);
            }
        }
        int num = orderMapper.insert(order);
        subOrderService.create(subOrderBOS);
        tallyService.orderActionSend(OrderActionTypeEnum.create, order.getOrderId(), subOrderBOS.stream().map(SubOrderBO::getSubOrderId).collect(Collectors.toList()), null);
        // 保存优惠券
        if (couponBOS.size() > 0) {
            couponService.create(couponBOS);
        }
        // 免押订单
        if (!StringUtils.isEmpty(orderBO.getSettlementInfo())) {
            creditOrderService.create(orderBO, subOrderBOS);
        }
        return order.getOrderId();
    }

    /**
     * 会员权益分摊
     *
     * @param orderBO
     * @param vipBO
     * @return
     */
    private List<VipEquity> getVipEquity(OrderBO orderBO, VipBO vipBO) {
        final List<VipEquity> vipEquities = new ArrayList<>(vipBO.getSubOrders().size());
        for (VipBO.SubOrder subOrder : vipBO.getSubOrders()) {
            VipEquity vipEquity = new VipEquity();
            vipEquity.setOrderId(orderBO.getOrderId());
            vipEquity.setSubOrderId(subOrder.getSubOrderId());
            vipEquity.setDiscount(vipBO.getDiscount());
            vipEquity.setUid(orderBO.getUid());
            vipEquity.setCreatedDt(LocalDateTime.now());
            vipEquity.setLastUpdDt(LocalDateTime.now());
            vipEquity.setVipLevel(vipBO.getLevel());
            vipEquity.setDiscountAmount(subOrder.getDiscountAmount());
            vipEquity.setUseBeamAmount(subOrder.getUseBeamAmount());
            vipEquity.setUseBeam(subOrder.getUseBeam());
            vipEquities.add(vipEquity);
        }
        return vipEquities;
    }

    @Override
    @Transactional
    public CreateOrderResVO createOrder(OrderBO orderBO, List<SubOrderBO> subOrderBOS, List<CouponBO> couponBOS, VipBO vipBO) throws BusinessException {
        // 重复下单判断
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("order_id", orderBO.getOrderId());
        conditions.eq("uid", orderBO.getUid());
        BusinessErrorInfoEnum.ORDER_CREATE_REPEAT.assertIsTrue(orderMapper.selectCount(conditions) == 0);

        // 如果是续费订单、尾款订单，验证关联的上一笔订单有效性
        OrderTypeEnum orderTypeEnum = EnumUtil.getEnumByCode(OrderTypeEnum.class, orderBO.getOrderType());
        if (orderTypeEnum == OrderTypeEnum.renew || orderTypeEnum == OrderTypeEnum.balance) {
            QueryWrapper<Order> originalConditions = new QueryWrapper<>();
            originalConditions.eq("order_id", orderBO.getOriginalOrderId());
            originalConditions.eq("order_status", OrderStatusEnum.paid.getCode());
            Long count = orderMapper.selectCount(originalConditions);
            BusinessErrorInfoEnum.ORDER_CREATE_ORIGINAL.assertIsTrue(count > 0);
        }

        // 保存主订单
        Order order = ObjectConvertUtil.convert(orderBO, Order.class).get();
        order.setOrderStatus(OrderStatusEnum.waiting_pay.getCode());
        order.setPayStatus(OrderPayStatusEnum.un_pay.getCode());
        order.setSubOrderCnt(subOrderBOS.size());
        order.setCreatedDt(orderBO.getCreateTime());
        order.setRefundTimes(0);        //初始退款次数为0
        // 支付过期时间，下单时间20分钟后
        if (order.getPayExpiryTime() == null) {
            order.setPayExpiryTime(order.getCreatedDt().plusMinutes(20));
        }
        // 计算主订单的优惠金额、实际支付金额
        int discountTotal = 0;//主订单优惠券金额
        if (couponBOS.size() > 0) {
            discountTotal = couponBOS.stream().mapToInt(CouponBO::getDiscount).sum();
            order.setDiscount(discountTotal);//优惠总金额，初始设为平台优惠金额
            order.setPlatformDiscount(discountTotal);//平台优惠金额
            order.setPayAmount(order.getOrderAmount() - order.getPlatformDiscount());//订单支付总金额=订单应收金额-平台优惠总金额
        } else {
            order.setPayAmount(order.getOrderAmount());
        }

        // 保存子订单
        // 计算出子订单的优惠金额（目前只支持平台单张优惠，分摊到各个子订单）
        if (couponBOS.size() > 0) {
            for (int i = 0; i < subOrderBOS.size(); i++) {
                SubOrderBO subOrderBO = subOrderBOS.get(i);
                if (i == subOrderBOS.size() - 1) {
                    subOrderBO.setDiscount(discountTotal - subOrderBOS.stream().mapToInt(SubOrderBO::getDiscount).sum());
                    subOrderBO.setPlatformDiscount(discountTotal - subOrderBOS.stream().mapToInt(SubOrderBO::getPlatformDiscount).sum());
                } else {
                    // 1、计算子订单所占总金额的比例，小数点后取10位，然后截取
                    // 2、总优惠金额*子订单比例，然后四舍五入取整
                    int divide = BigDecimal.valueOf(subOrderBO.getSubOrderAmount()).divide(BigDecimal.valueOf(order.getOrderAmount()), 10, BigDecimal.ROUND_DOWN).multiply(BigDecimal.valueOf(discountTotal)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
                    subOrderBO.setDiscount(divide);
                    subOrderBO.setPlatformDiscount(divide);
                }
                subOrderBO.setIsCoupon(subOrderBO.getDiscount() > 0 ? 1 : 0);
            }
        }
        CreateOrderResVO createOrderResVO = new CreateOrderResVO();
        createOrderResVO = ObjectConvertUtil.convert(order, CreateOrderResVO.class).get();
        // 会员权益分摊
        if (vipBO != null) {
            final List<VipEquity> vipEquities = getVipEquity(orderBO, vipBO);
            if (CollectionUtils.isNotEmpty(vipEquities)) {
                final int sumVipDiscount = vipEquities.stream().mapToInt(VipEquity::getDiscountAmount).sum();
                final int sumVipUseBeam = vipEquities.stream().mapToInt(VipEquity::getUseBeamAmount).sum();
                order.setDiscount(order.getDiscount() + sumVipDiscount + sumVipUseBeam);
                order.setPlatformDiscount(order.getDiscount());
                order.setPayAmount(order.getOrderAmount() - order.getPlatformDiscount());
                order.setIsCoupon(order.getDiscount() > 0 ? 0 : 1);
                subOrderBOS.forEach(s -> {
                    final VipEquity vipEquity = vipEquities.stream().filter(v -> s.getOrderId().equals(v.getOrderId()) && v.getSubOrderId().equals(s.getSubOrderId())).findFirst().get();
                    s.setDiscount(s.getDiscount() + vipEquity.getDiscountAmount() + vipEquity.getUseBeamAmount());
                    s.setPlatformDiscount(s.getDiscount());
                });

                final CreateOrderResVO.VipInfo vipInfo = new CreateOrderResVO.VipInfo();
                vipInfo.setDiscount(vipBO.getDiscount());
                vipInfo.setDiscountAmount(sumVipDiscount);
                vipInfo.setUseBeam(vipBO.getUseBeam());
                vipInfo.setUseBeamAmount(sumVipUseBeam);
                vipInfo.setLevel(vipBO.getLevel());
                createOrderResVO.setVipInfo(vipInfo);
                createOrderResVO.setDiscount(order.getDiscount());
                createOrderResVO.setPlatformDiscount(order.getDiscount() - sumVipDiscount - sumVipUseBeam);
                vipEquityService.saveBatch(vipEquities);
            }
        }
        int num = orderMapper.insert(order);
        subOrderService.create(subOrderBOS);
        tallyService.orderActionSend(OrderActionTypeEnum.create, order.getOrderId(), subOrderBOS.stream().map(SubOrderBO::getSubOrderId).collect(Collectors.toList()), null);
        // 保存优惠券
        if (couponBOS.size() > 0) {
            couponService.create(couponBOS);
        }
        // 免押订单
        if (!StringUtils.isEmpty(orderBO.getSettlementInfo())) {
            creditOrderService.create(orderBO, subOrderBOS);
        }

        List<CreateOrderResVO.Coupon> resCoupons = couponBOS.stream().map(couponBO -> {
            CreateOrderResVO.Coupon coupon = ObjectConvertUtil.convert(couponBO, CreateOrderResVO.Coupon.class).get();
            return coupon;
        }).collect(Collectors.toList());
        createOrderResVO.setCoupons(resCoupons);

        List<SubOrder> subOrders = subOrderBOS.stream().map(subOrderBO -> {
            SubOrder subOrder = ObjectConvertUtil.convert(subOrderBO, SubOrder.class).get();
            subOrder.setSubOrderStatus(OrderStatusEnum.waiting_pay.getCode());
            subOrder.setPayAmount(subOrder.getSubOrderAmount() - subOrder.getPlatformDiscount());//子订单支付金额=子订单应收金额-子订单平台优惠金额
            subOrder.setCreatedDt(subOrderBO.getCreateTime());
            subOrder.setRefundAmount(0);
            return subOrder;
        }).collect(Collectors.toList());
        createOrderResVO.setSubOrders(subOrders);

        return createOrderResVO;
    }

    @Override
    public String createDeductionOrder(OrderBO orderBO, List<SubOrderBO> subOrderBOS) throws BusinessException {
        BusinessErrorInfoEnum.ORDER_CREATE_CHANNEL_ERROR.assertIsTrue(orderBO.getPayChannel().equals(PaymentChannelEnum.UMS.getCode()));
        // 重复下单判断
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("order_id", orderBO.getOrderId());
        conditions.eq("uid", orderBO.getUid());
        BusinessErrorInfoEnum.ORDER_CREATE_REPEAT.assertIsTrue(orderMapper.selectCount(conditions) == 0);

        // 保存主订单
        Order order = ObjectConvertUtil.convert(orderBO, Order.class).get();
        order.setOrderStatus(OrderStatusEnum.waiting_pay.getCode());
        order.setPayStatus(OrderPayStatusEnum.un_pay.getCode());
        order.setSubOrderCnt(subOrderBOS.size());
        order.setCreatedDt(orderBO.getCreateTime());
        order.setRefundTimes(0);        //初始退款次数为0
        // 支付过期时间，下单时间20分钟后
        if (order.getPayExpiryTime() == null) {
            order.setPayExpiryTime(order.getCreatedDt().plusMinutes(20));
        }
        order.setDiscount(orderBO.getOrderAmount());//优惠总金额，初始设为平台优惠金额
        order.setPlatformDiscount(order.getDiscount());//平台优惠金额
        order.setPayAmount(0);//订单支付总金额=订单应收金额-平台优惠总金额

        // 保存子订单
        // 计算出子订单的优惠金额（目前只支持平台单张优惠，分摊到各个子订单）
        for (SubOrderBO subOrderBO : subOrderBOS) {
            subOrderBO.setDiscount(subOrderBO.getSubOrderAmount());
            subOrderBO.setPlatformDiscount(subOrderBO.getDiscount());
            subOrderBO.setIsCoupon(subOrderBO.getDiscount() > 0 ? 1 : 0);
        }
        order.setIsCoupon(order.getDiscount() > 0 ? 0 : 1);
        int num = orderMapper.insert(order);
        subOrderService.create(subOrderBOS);
        tallyService.orderActionSend(OrderActionTypeEnum.create, order.getOrderId(), subOrderBOS.stream().map(SubOrderBO::getSubOrderId).collect(Collectors.toList()), null);

        // 免押订单
        if (!StringUtils.isEmpty(orderBO.getSettlementInfo())) {
            creditOrderService.create(orderBO, subOrderBOS);
        }
        return order.getOrderId();
    }

    @Override
    public OpenCashierResVO initCashier(String uid, CashierVO cashierVO) throws BusinessException {

        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("uid", uid);
        conditions.in("order_id", cashierVO.getOrderIds());
        conditions.eq("order_status", OrderStatusEnum.waiting_pay.getCode());
        List<Order> orders = orderMapper.selectList(conditions);
        BusinessErrorInfoEnum.ORDER_CASHIER_STATUS.assertIsTrue(orders.size() == cashierVO.getOrderIds().length);

        // 获取订单的支付渠道，并判断禁止跨渠道支付，以及判断订单是否支付超时
        Long orgPaymentChannelId = orders.get(0).getOrgPaymentChannelId();
        for (Order order : orders) {
            BusinessErrorInfoEnum.ORDER_CASHIER_CHANNEL_DIFF.assertIsTrue(orgPaymentChannelId.equals(order.getOrgPaymentChannelId()));
            BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT_CLOSE.assertIsTrue(order.getPayExpiryTime().isAfter(LocalDateTime.now()));
            BusinessErrorInfoEnum.ORDER_PAY_NOT_ALLOW_ORDER_TYPE_ERROR.assertIsTrue(StringUtils.isEmpty(order.getBatchId()));
        }

        // 判断优惠券，目前只允许带一张优惠券支付，如果是合单支付，不允许带优惠券
        List<Coupon> coupons = couponService.find(cashierVO.getOrderIds());
//        BusinessErrorInfoEnum.ORDER_CASHIER_COUPON_ONE.assertIsTrue(coupons.size() <= 1);
//        BusinessErrorInfoEnum.ORDER_CASHIER_MERGE_COUPON.assertIsTrue(orders.size() == 1 || coupons.size() == 0);

        OpenCashierResVO cashierResVO = new OpenCashierResVO();
        cashierResVO.setOrderSource(orders.size() > 1 ? OrderSourceEnum.CONSOLIDATION.getCode() : orders.get(0).getOrderSource());
        cashierResVO.setType(EnumUtil.getEnumByCode(OrderSourceEnum.class, cashierResVO.getOrderSource()).name().toLowerCase());
        cashierResVO.setOrderName(orders.get(0).getOrderName());
        final Set<String> batchIds = orders.stream().filter(o -> !StringUtils.isEmpty(o.getBatchId())).map(Order::getBatchId).collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(batchIds) && batchIds.size() > 1) {
            // 有多个批次id，或者不带批次号的订单与带批次号的订单同时支付
            BusinessErrorInfoEnum.ORDER_PAY_NOT_ALLOW_MERGE_ERROR.assertFail();
        }

        if (CollectionUtils.isNotEmpty(batchIds) && batchIds.size() == 1) {
            // 只有一个批次号
            OrderBatch orderBatch = orderBatchService.findByBatchId(orders.get(0).getBatchId());
            cashierResVO.setOrderName(orderBatch.getOrderName());
        }

        // 某些场景需要在收银台上判断判断客户端版本是否支持ums支付
        String payChannel = checkPayChannelByClientVersion(cashierVO, conditions, orders, orders.get(0).getPayChannel());
        cashierResVO.setPayChannel(payChannel);

        LocalDateTime payExpiryTime = orders.stream().map(Order::getPayExpiryTime).min(Comparator.comparing(date -> date.toEpochSecond(ZoneOffset.of("+8")))).get();
        cashierResVO.setPayLeftTime(payExpiryTime.toEpochSecond(ZoneOffset.of("+8")) - LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));

        long discount = coupons.stream().mapToInt(Coupon::getDiscount).sum();
        cashierResVO.setDiscount(new BigDecimal(discount).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
        long payAmount = orders.stream().mapToInt(Order::getPayAmount).sum();
        cashierResVO.setPayAmount(new BigDecimal(payAmount).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
        long orderAmount = orders.stream().mapToInt(Order::getOrderAmount).sum();
        cashierResVO.setOrderAmount(new BigDecimal(orderAmount).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));

        orders.forEach(order -> cashierResVO.getOrders().add(new OpenCashierResVO.Order(order.getOrderId(), order.getOrderName(), new BigDecimal(order.getOrderAmount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP), new BigDecimal(order.getPayAmount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP))));

        // 到支付中心根据支付渠道获取支付方式
        final List<PaymentPayWayQueryResponse> channels = this.getPayWayListByPayChannel(orders.get(0).getOrgPaymentChannelId(),
                orders.get(0).getPayChannel(), null, null);

        cashierResVO.setPaymentWays(channels);
        long platformDiscount = orders.stream().mapToInt(Order::getPlatformDiscount).sum();
        cashierResVO.setNotPay(0);
        if (platformDiscount == orderAmount && payAmount == 0) {
            // 优惠全额抵扣了，不需要支付
            // 直接调用支付接口
            final OpenOrderPayVO payVO = new OpenOrderPayVO();
            payVO.setUid(uid);
            payVO.setPayWay(PaymentWayEnum.WECHAT.getCode());
            payVO.setPayScene(PaySceneEnum.APP.getCode());
            payVO.setOrderId(orders.stream().map(Order::getOrderId).collect(Collectors.toList()).get(0));
            this.pay(uid, payVO);
            cashierResVO.setNotPay(1);
        }
        return cashierResVO;
    }

    @Override
    public OrderPrePayResVO pay(String uid, OpenOrderPayVO orderPayVO) throws BusinessException {
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        //conditions.eq("uid", uid);
        conditions.eq("order_id", orderPayVO.getOrderId());
        conditions.eq("order_status", OrderStatusEnum.waiting_pay.getCode());
        conditions.eq("pay_status", OrderPayStatusEnum.un_pay.getCode());
        List<Order> orders = orderMapper.selectList(conditions);
        // 检查订单状态、订单支付失效时间、订单禁止跨渠道支付
        BusinessErrorInfoEnum.ORDER_PAY_STATUS.assertIsTrue(orders.size() == 1);
        //BusinessErrorInfoEnum.ORDER_PAY_USER_ERROR.assertEquals(orders.get(0).getUid(),uid);
        String payChannel = orders.get(0).getPayChannel();
        if (StringUtils.isEmpty(uid) || uid.equals("0")) {
            uid = orders.get(0).getUid();
        }
        if (!StringUtils.isEmpty(uid) && !uid.equals(orders.get(0).getUid())) {
            log.warn("创建订单与支付不是同一个人，创建订单：{},支付：{}", orders.get(0).getUid(), uid);
        }
        for (Order order : orders) {
            BusinessErrorInfoEnum.ORDER_PAY_NOT_ALLOW_ORDER_TYPE_ERROR.assertIsTrue(!OrderTypeEnum.deduction.getCode().equalsIgnoreCase(order.getOrderType()));
            BusinessErrorInfoEnum.ORDER_PAY_CHANNEL.assertIsTrue(payChannel.equals(order.getPayChannel()));
            BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT_CLOSE.assertIsTrue(order.getPayExpiryTime().isAfter(LocalDateTime.now()));
            BusinessErrorInfoEnum.ORDER_PAY_NOT_ALLOW_ORDER_TYPE_ERROR.assertIsTrue(StringUtils.isEmpty(order.getBatchId()));
        }

        // 组织返回的支付信息
        OrderPrePayResVO prePayResVO = new OrderPrePayResVO();
        prePayResVO.setPayWay(orderPayVO.getPayWay());
        prePayResVO.setOpenid(orderPayVO.getOpenid());
        prePayResVO.setOrderName(orders.get(0).getOrderName());
        prePayResVO.setPayChannel(orders.get(0).getPayChannel());

        LocalDateTime payExpiryTime = orders.stream().map(Order::getPayExpiryTime).min(Comparator.comparing(date -> date.toEpochSecond(ZoneOffset.of("+8")))).get();
        prePayResVO.setPayLeftTime(payExpiryTime.toEpochSecond(ZoneOffset.of("+8")) - LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));

        long discount = orders.stream().mapToInt(Order::getPlatformDiscount).sum();//平台优惠金额
        prePayResVO.setDiscount(new BigDecimal(discount).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP));
        long payAmount = orders.stream().mapToInt(Order::getPayAmount).sum();
        prePayResVO.setPayAmount(new BigDecimal(payAmount).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP));
        long orderAmount = orders.stream().mapToInt(Order::getOrderAmount).sum();
        prePayResVO.setOrderAmount(new BigDecimal(orderAmount).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP));

        List<OrderPrePayResVO.Order> resOrders = orders.stream().map(order -> new OrderPrePayResVO.Order(order.getOrderId(), order.getOrderName(), new BigDecimal(order.getOrderAmount()).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP), new BigDecimal(order.getPayAmount()).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP), EnumUtil.getEnumByCode(OrderSourceEnum.class, order.getOrderSource()).name().toLowerCase())).collect(Collectors.toList());
        prePayResVO.setOrders(resOrders);
        // 针对该用户加锁，5秒后自动释放，并发等待5秒获取，5秒后未获取抛出超时异常
        try (Lock lock = new Lock(Lock.LOCK_ORDER_PAY, orders.get(0).getUid(), 5, 5)) {
            BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT.assertIsTrue(lock.getLockRes());

            // 判断并返回缓存中的预支付信息,缓存时长为半小时
            Optional<String> prePayInfo = this.getCachePrePayInfo(orders.get(0).getUid(), orderPayVO, orders, null);
            if (prePayInfo.isPresent()) {
                // 判断是否为银联+支付宝+app支付，需要重新生成一笔支付流水,坑爹
                if (orders.get(0).getPayChannel().equalsIgnoreCase(PaymentChannelEnum.UMS.getCode()) && orderPayVO.getPayScene().equalsIgnoreCase(PaySceneEnum.APP.getCode()) && orderPayVO.getPayWay().equalsIgnoreCase(PaymentWayEnum.ALIPAY.getCode())) {
                    log.info("作废支付宝缓存预支付信息，重新生成流水");
                    // TODO 调用支付中心查询该流水的支付结果，已支付则抛出异常
                    String cacheUniqueKey = orderIdHelper.getUniqueIdByOrderIds(orders.get(0).getUid(), orders.stream().map(Order::getOrderId).collect(Collectors.toList()), orderPayVO.getPayWay(), orderPayVO.getPayScene(), null);
                    OrderCache.delPrePayInfo(cacheUniqueKey);

                } else {
                    if (discount == orderAmount && payAmount == 0) {
                        // 优惠全额抵扣了，不需要支付
                        prePayResVO.setPayInfo("not_pay");
                    } else {
                        // 正常支付订单，需要支付
                        prePayResVO.setPayInfo(prePayInfo.get());
                    }
                    return prePayResVO;
                }
            }

            // 查询订单的优惠券信息
            List<Coupon> coupons = couponService.find(new String[]{orderPayVO.getOrderId()});
            String spInfo = coupons.stream().map(coupon -> coupon.getCouponCode() + "," + coupon.getCouponName()).collect(Collectors.toList()).stream().collect(Collectors.joining(","));

            String orderSource = orders.size() > 1 ? OrderSourceEnum.CONSOLIDATION.getCode() : orders.get(0).getOrderSource();
            String paymentId = orderIdHelper.getPaymentId(uid);
            OrderPayDTO orderPayDTO = new OrderPayDTO();
            orderPayDTO.setUid(uid);
            orderPayDTO.setOrderName(orders.get(0).getOrderName());
            orderPayDTO.setUserTel(orders.get(0).getMobile());
            orderPayDTO.setPaymentId(paymentId);
            orderPayDTO.setBusinessType(orderSource);
            orderPayDTO.setTs(LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
            orderPayDTO.setAmount(orders.stream().mapToInt(Order::getPayAmount).sum());
            orderPayDTO.setOrderAmount(orders.stream().mapToInt(Order::getOrderAmount).sum());
            orderPayDTO.setDiscount(orders.stream().mapToInt(Order::getPlatformDiscount).sum());
            orderPayDTO.setPayScene(orderPayVO.getPayScene());
            orderPayDTO.setPayChannel(payChannel);
            orderPayDTO.setPayWay(orderPayVO.getPayWay());
            orderPayDTO.setSpInfo(spInfo);
            orderPayDTO.setOpenid(orderPayVO.getOpenid());
            orderPayDTO.setSubAppId(orderPayVO.getSubAppId());
            orderPayDTO.setReturnUrl(orderPayVO.getReturnUrl());
            // 如果是小程序支付，没有传openid，默认使用创建订单时的openid
            if (OrderSceneEnum.miniapp.name().equals(orderPayVO.getPayScene()) && StringUtils.isEmpty(orderPayVO.getOpenid())) {
                orderPayDTO.setOpenid(orders.get(0).getOpenid());
            }
            // 如果支付方式是微信，判断是否有goods_tag
            if (PaymentWayEnum.WECHAT.getCode().equals(orderPayVO.getPayWay()) && !StringUtils.isEmpty(orders.get(0).getGoodsTag())) {
                orderPayDTO.setGoodsTag(orders.get(0).getGoodsTag());
            }
            // 设置主商户
            orderPayDTO.setMerchantCode(orderCenterConfigProperties.getMainMerchant().getCode());
            orderPayDTO.setMerchantName(orderCenterConfigProperties.getMainMerchant().getName());

            // 组织子订单数据
            List<SubOrder> subOrders = subOrderService.find(new String[]{orderPayVO.getOrderId()});
            List<OrderPayDTO.SubOrderPayDTO> subOrderPayDTOS = subOrders.stream().map(subOrder -> {
                OrderPayDTO.SubOrderPayDTO subOrderPayDTO = ObjectConvertUtil.convert(subOrder, OrderPayDTO.SubOrderPayDTO.class).get();
                subOrderPayDTO.setOrderAmount(subOrder.getSubOrderAmount());
                subOrderPayDTO.setAmount(subOrder.getPayAmount());
                return subOrderPayDTO;
            }).collect(Collectors.toList());
            orderPayDTO.setSubOrderPayDTOS(subOrderPayDTOS);

            // 调用支付中心服务
            Result<UnifiedOrderResVO> result;
            if (discount == orderAmount && payAmount == 0 && CollectionUtils.isNotEmpty(coupons)) {
                // 优惠全额抵扣了，不需要支付
                orderPayDTO.setDiscount(0);
                orderPayDTO.setPayWay(PaymentWayEnum.DEDUCTION.getCode());
                orderPayDTO.setPayScene(PaySceneEnum.DEDUCTION.getCode());
                result = paymentFeignService.deductionUnified(orderPayDTO);
            } else {
                // 正常支付订单，需要支付
                result = paymentFeignService.unifiedOrder(orderPayDTO);
            }

            log.info("支付单号:{},支付返回信息:{}", paymentId, result);
            BusinessErrorInfoEnum.ORDER_PAY_SERVER_ERROR.assertIsTrue(result.getRet() == 0 && result.getErrorCode() == 0, StringUtils.isNotEmpty(result.getMsg()) ? result.getMsg() : "");

            // 缓存预支付信息
//            TreeSet<String> orderIds = Stream.of(orderPayVO.getOrderIds()).collect(Collectors.toCollection(TreeSet::new));
            String cacheUniqueKey = orderIdHelper.getUniqueIdByOrderIds(orders.get(0).getUid(), orders.stream().map(Order::getOrderId).collect(Collectors.toList()), orderPayVO.getPayWay(), orderPayVO.getPayScene(), null);
            log.info("支付订单，获取cacheUniqueKey：{}", cacheUniqueKey);
            OrderCache.prePayInfo(cacheUniqueKey, result.getData());

            // 缓存提交的支付参数
            PayParamsCacheBO payParamsCacheBO = ObjectConvertUtil.convert(orderPayVO, PayParamsCacheBO.class).get();
            payParamsCacheBO.setUid(orders.get(0).getUid());
            OrderCache.payParams(paymentId, payParamsCacheBO);

            if (discount == orderAmount && payAmount == 0 && CollectionUtils.isNotEmpty(coupons)) {
                // 优惠全额抵扣了，不需要支付
                prePayResVO.setPayInfo("not_pay");
            } else {
                // 正常支付订单，需要支付
                prePayResVO.setPayInfo(result.getData().getPayInfo());
            }
            return prePayResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT, null, e.getMessage(), e);
        }
    }

    @Override
    public Boolean zeroYuanDeductionPay(String uid, OpenOrderPayVO orderPayVO) throws BusinessException {
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        //conditions.eq("uid", uid);
        conditions.eq("order_id", orderPayVO.getOrderId());
        conditions.eq("order_status", OrderStatusEnum.waiting_pay.getCode());
        conditions.eq("pay_status", OrderPayStatusEnum.un_pay.getCode());
        conditions.eq("order_type", OrderTypeEnum.deduction.getCode());
        List<Order> orders = orderMapper.selectList(conditions);
        BusinessErrorInfoEnum.ORDER_PAY_STATUS.assertIsTrue(orders.size() == 1);
        BusinessErrorInfoEnum.ORDER_DEDUCTION_PAY_CHECK_ERROR.assertIsTrue(deductionPayCheckService.deductionPayCheck(new DeductionPayCheckDTO(orderPayVO.getOrderId(), uid, orders.get(0).getOrderSource(), orders.get(0).getOrderAmount())));
        // 针对该用户加锁，5秒后自动释放，并发等待5秒获取，5秒后未获取抛出超时异常
        try (Lock lock = new Lock(Lock.LOCK_ORDER_PAY, orders.get(0).getUid(), 5, 5)) {
            BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT.assertIsTrue(lock.getLockRes());

            // 判断并返回缓存中的预支付信息,缓存时长为半小时
            Optional<String> prePayInfo = this.getCachePrePayInfo(orders.get(0).getUid(), orderPayVO, orders, null);
            if (prePayInfo.isPresent()) {
                // 重复调用
                return true;
            }
            // 查询订单的优惠券信息
            List<Coupon> coupons = couponService.find(new String[]{orderPayVO.getOrderId()});
            String spInfo = coupons.stream().map(coupon -> coupon.getCouponCode() + "," + coupon.getCouponName()).collect(Collectors.toList()).stream().collect(Collectors.joining(","));

            String orderSource = orders.size() > 1 ? OrderSourceEnum.CONSOLIDATION.getCode() : orders.get(0).getOrderSource();
            String paymentId = orderIdHelper.getPaymentId(uid);
            OrderPayDTO orderPayDTO = new OrderPayDTO();
            orderPayDTO.setUid(uid);
            orderPayDTO.setOrderName(orders.get(0).getOrderName());
            orderPayDTO.setUserTel(orders.get(0).getMobile());
            orderPayDTO.setPaymentId(paymentId);
            orderPayDTO.setBusinessType(orderSource);
            orderPayDTO.setTs(LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
            orderPayDTO.setAmount(orders.stream().mapToInt(Order::getOrderAmount).sum());
            orderPayDTO.setOrderAmount(orders.stream().mapToInt(Order::getOrderAmount).sum());
            orderPayDTO.setDiscount(0);
            orderPayDTO.setPayScene(orderPayVO.getPayScene());
            orderPayDTO.setPayChannel(orders.get(0).getPayChannel());
            orderPayDTO.setPayWay(orderPayVO.getPayWay());
            orderPayDTO.setSpInfo(spInfo);
            orderPayDTO.setOpenid(orderPayVO.getOpenid());
            orderPayDTO.setSubAppId(orderPayVO.getSubAppId());
            orderPayDTO.setReturnUrl(orderPayVO.getReturnUrl());
            // 设置主商户
            orderPayDTO.setMerchantCode(orderCenterConfigProperties.getMainMerchant().getCode());
            orderPayDTO.setMerchantName(orderCenterConfigProperties.getMainMerchant().getName());

            // 组织子订单数据
            List<SubOrder> subOrders = subOrderService.find(new String[]{orderPayVO.getOrderId()});
            List<OrderPayDTO.SubOrderPayDTO> subOrderPayDTOS = subOrders.stream().map(subOrder -> {
                OrderPayDTO.SubOrderPayDTO subOrderPayDTO = ObjectConvertUtil.convert(subOrder, OrderPayDTO.SubOrderPayDTO.class).get();
                subOrderPayDTO.setOrderAmount(subOrder.getSubOrderAmount());
                subOrderPayDTO.setAmount(subOrder.getSubOrderAmount());
                return subOrderPayDTO;
            }).collect(Collectors.toList());
            orderPayDTO.setSubOrderPayDTOS(subOrderPayDTOS);

            // 调用支付中心服务
            Result<UnifiedOrderResVO> result = paymentFeignService.deductionUnified(orderPayDTO);
            log.info("支付单号:{},支付返回信息:{}", paymentId, result);
            BusinessErrorInfoEnum.ORDER_PAY_SERVER_ERROR.assertIsTrue(result.getRet() == 0 && result.getErrorCode() == 0, StringUtils.isNotEmpty(result.getMsg()) ? result.getMsg() : "");

            // 缓存预支付信息
//            TreeSet<String> orderIds = Stream.of(orderPayVO.getOrderIds()).collect(Collectors.toCollection(TreeSet::new));
            String cacheUniqueKey = orderIdHelper.getUniqueId(orders.get(0).getUid(), orderPayVO.getOrderId(), orderPayVO.getPayWay(), orderPayVO.getPayScene());
            log.info("支付订单，获取cacheUniqueKey：{}", cacheUniqueKey);
            OrderCache.prePayInfo(cacheUniqueKey, result.getData());

            // 缓存提交的支付参数
            PayParamsCacheBO payParamsCacheBO = ObjectConvertUtil.convert(orderPayVO, PayParamsCacheBO.class).get();
            payParamsCacheBO.setUid(orders.get(0).getUid());
            OrderCache.payParams(paymentId, payParamsCacheBO);
            return true;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT, null, e.getMessage(), e);
        }
    }


    @Override
    @Transactional
    public PayResultHandlerResultBO savePayResult(PayResultMsgBO payNotifyBO) throws BusinessException {
        Optional<PayParamsCacheBO> payParams = OrderCache.getPayParams(payNotifyBO.getPaymentId());
        BusinessErrorInfoEnum.ORDER_PAY_NOTIFY_NO_PAYMENT.assertIsTrue(payParams.isPresent());

        // 针对该用户加锁，5秒后自动释放，并发等待60秒获取，60秒后未获取抛出超时异常
        try (Lock lock = new Lock(Lock.LOCK_NOTIFY_PAY, payNotifyBO.getPaymentId(), 60, 5)) {
            BusinessErrorInfoEnum.ORDER_PAY_NOTIFY_NO_LOCK.assertIsTrue(lock.getLockRes());

            String payStatus = payNotifyBO.getStatus().equalsIgnoreCase("fail") ? OrderPayStatusEnum.pay_fail.getCode() : OrderPayStatusEnum.paid.getCode();

            PayResultHandlerResultBO result = new PayResultHandlerResultBO();
            result.setFlowId(payNotifyBO.getFlowId());
            result.setPayTime(payNotifyBO.getPayTime());
            result.setPayStatus(payStatus);
            result.setPayAmount(payNotifyBO.getAmount());
            result.setPayScene(payNotifyBO.getPayScene());
            result.setPayChannel(payNotifyBO.getPaymentChannel());
            result.setPayWay(payNotifyBO.getPaymentWay());
            result.setIsCoupon(null != payNotifyBO.getCouponAmount() && payNotifyBO.getCouponAmount() > 0 ? 1 : 0);
            result.setDiscount(payNotifyBO.getCouponAmount());
            QueryWrapper<Order> conditions = new QueryWrapper<>();
            //conditions.eq("uid", payParams.get().getUid());
            conditions.in("order_id", payParams.get().getOrderIds());

            List<Order> orders = orderMapper.selectList(conditions);
            BusinessErrorInfoEnum.ORDER_PAY_NOTIFY_NO_DATA.assertIsTrue(orders.size() > 0);

            long validNum = orders.stream().filter(order -> !(OrderStatusEnum.waiting_pay.equals(order.getOrderStatus()) && OrderPayStatusEnum.un_pay.equals(order.getPayStatus()))).count();
            // 主订单集合
            final List<PayResultOrderBO> payResultOrderBOS = new ArrayList<>();
            // 检查订单状态是否正确
            if (validNum == orders.size()) {
                int discountTotal = payNotifyBO.getCouponAmount();//渠道优惠金额
                if (orders.stream().mapToInt(Order::getOrderAmount).sum() == orders.stream().mapToInt(Order::getPlatformDiscount).sum()) {
                    //付款前已经全额抵扣了，不用再处理优惠信息了
                    discountTotal = 0;
                }
                log.info("修改订单优惠信息：渠道优惠金额discountTotal:{}", discountTotal);
                if (discountTotal > 0) {
                    int sumOrderAmount = orders.stream().mapToInt(Order::getOrderAmount).sum();
                    int divideTotal = 0;//已经平摊掉的渠道优惠
                    for (int i = 0; i < orders.size(); i++) {
                        int currentOrderDiscount = 0;
                        //总优惠金额=平台优惠金额+渠道优惠金额
                        if (i == orders.size() - 1) {
                            currentOrderDiscount = discountTotal - divideTotal;
                            orders.get(i).setDiscount(orders.get(i).getPlatformDiscount() + currentOrderDiscount);
                        } else {
                            currentOrderDiscount = BigDecimal.valueOf(orders.get(i).getOrderAmount()).divide(BigDecimal.valueOf(sumOrderAmount), 10, BigDecimal.ROUND_DOWN).multiply(BigDecimal.valueOf(discountTotal)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();//每笔订单分摊到的渠道优惠
                            orders.get(i).setDiscount(orders.get(i).getPlatformDiscount() + currentOrderDiscount);
                            divideTotal += (currentOrderDiscount);
                        }
                        log.info("订单{}的总优惠金额:{}", orders.get(i).getOrderId(), orders.get(i).getDiscount());
                        orders.get(i).setIsCoupon(orders.get(i).getDiscount() > 0 ? 0 : 1);
                        orders.get(i).setPayAmount(orders.get(i).getOrderAmount() - orders.get(i).getDiscount());
                        // 修改订单状态为支付成功
                        QueryWrapper<Order> updateCondition = new QueryWrapper<>();
                        //conditions.eq("uid", payParams.get().getUid());
                        updateCondition.eq("order_id", orders.get(i).getOrderId());
                        updateCondition.eq("id", orders.get(i).getId());
                        Order updateOrder = new Order();
                        updateOrder.setPaymentId(payNotifyBO.getPaymentId());
                        updateOrder.setFlowId(payNotifyBO.getFlowId());
                        updateOrder.setPayTime(payNotifyBO.getPayTime());
                        updateOrder.setOrderStatus(OrderStatusEnum.paid.getCode());
                        updateOrder.setPayStatus(OrderPayStatusEnum.paid.getCode());
                        if (!StringUtils.isEmpty(payNotifyBO.getStatus()) && payNotifyBO.getStatus().equalsIgnoreCase("fail")) {
                            updateOrder.setOrderStatus(OrderStatusEnum.waiting_pay.getCode());
                            updateOrder.setPayStatus(OrderPayStatusEnum.pay_fail.getCode());
                        }
                        updateOrder.setPayWay(payParams.get().getPayWay());
                        updateOrder.setPayScene(payParams.get().getPayScene());
                        updateOrder.setBuyerUid(payNotifyBO.getUserId());
                        updateOrder.setDiscount(orders.get(i).getDiscount());
                        updateOrder.setIsCoupon(orders.get(i).getDiscount() > 0 ? 0 : 1);
                        if (!StringUtils.isEmpty(payNotifyBO.getPaymentChannel()) && !payNotifyBO.getPaymentChannel().equals(orders.get(i).getPayChannel())) {
                            // 更新支付渠道
                            updateOrder.setPayChannel(payNotifyBO.getPaymentChannel());
                        }
                        // 更新订单优惠金额、实际支付金额
                        updateOrder.setPayAmount(orders.get(i).getPayAmount());

                        // 主订单信息
                        final PayResultOrderBO payResultOrderBO = new PayResultOrderBO();
                        payResultOrderBO.setOrderId(orders.get(i).getOrderId());
                        payResultOrderBO.setPaymentId(updateOrder.getPaymentId());
                        payResultOrderBO.setFlowId(updateOrder.getFlowId());
                        payResultOrderBO.setUid(orders.get(i).getUid());
                        payResultOrderBO.setPayTime(updateOrder.getPayTime());
                        payResultOrderBO.setOrderStatus(updateOrder.getOrderStatus());
                        payResultOrderBO.setPayStatus(updateOrder.getPayStatus());
                        payResultOrderBO.setPayAmount(updateOrder.getPayAmount());
                        payResultOrderBO.setPayScene(updateOrder.getPayScene());
                        payResultOrderBO.setPayWay(updateOrder.getPayWay());
                        payResultOrderBO.setIsCoupon(updateOrder.getIsCoupon());
                        payResultOrderBO.setDiscount(updateOrder.getDiscount());
                        payResultOrderBO.setPlatformDiscount(orders.get(i).getPlatformDiscount());
                        payResultOrderBO.setClientId(orders.get(i).getClientId());

                        int num = orderMapper.update(updateOrder, updateCondition);
                        // 修改子订单已支付
                        log.info("修改子订单订单优惠信息：orders.get(i).getDiscount():{}", orders.get(i).getDiscount());

                        final List<PayResultSubOrderBO> subOrderBOS = subOrderService.paid(orders.get(i).getOrderId(), payNotifyBO.getSubFlows(), currentOrderDiscount, updateOrder.getOrderStatus());//传渠道优惠参与子订单渠道优惠均摊
                        payResultOrderBO.setSubOrders(subOrderBOS);
                        payResultOrderBOS.add(payResultOrderBO);
                    }
                } else {
                    // 修改订单状态为支付成功
                    Order updateOrder = new Order();
                    updateOrder.setPaymentId(payNotifyBO.getPaymentId());
                    updateOrder.setFlowId(payNotifyBO.getFlowId());
                    updateOrder.setPayTime(payNotifyBO.getPayTime());
                    updateOrder.setOrderStatus(OrderStatusEnum.paid.getCode());
                    updateOrder.setPayStatus(OrderPayStatusEnum.paid.getCode());
                    if (!StringUtils.isEmpty(payNotifyBO.getStatus()) && payNotifyBO.getStatus().equalsIgnoreCase("fail")) {
                        updateOrder.setOrderStatus(OrderStatusEnum.waiting_pay.getCode());
                        updateOrder.setPayStatus(OrderPayStatusEnum.pay_fail.getCode());
                    }
                    if (!StringUtils.isEmpty(payNotifyBO.getPaymentChannel()) && !payNotifyBO.getPaymentChannel().equals(orders.get(0).getPayChannel())) {
                        // 更新支付渠道
                        updateOrder.setPayChannel(payNotifyBO.getPaymentChannel());
                    }
                    updateOrder.setPayWay(payParams.get().getPayWay());
                    updateOrder.setPayScene(payParams.get().getPayScene());
                    updateOrder.setBuyerUid(payNotifyBO.getUserId());
                    int num = orderMapper.update(updateOrder, conditions);
                    for (Order order : orders) {
                        // 主订单信息
                        final PayResultOrderBO payResultOrderBO = new PayResultOrderBO();
                        payResultOrderBO.setOrderId(order.getOrderId());
                        payResultOrderBO.setPaymentId(updateOrder.getPaymentId());
                        payResultOrderBO.setFlowId(updateOrder.getFlowId());
                        payResultOrderBO.setUid(order.getUid());
                        payResultOrderBO.setPayTime(updateOrder.getPayTime());
                        payResultOrderBO.setOrderStatus(updateOrder.getOrderStatus());
                        payResultOrderBO.setPayStatus(updateOrder.getPayStatus());
                        payResultOrderBO.setPayAmount(order.getOrderAmount());
                        payResultOrderBO.setPayScene(updateOrder.getPayScene());
                        payResultOrderBO.setPayWay(updateOrder.getPayWay());
                        payResultOrderBO.setIsCoupon(0);
                        payResultOrderBO.setDiscount(0);
                        payResultOrderBO.setPlatformDiscount(0);
                        payResultOrderBO.setClientId(order.getClientId());
                        // 修改子订单已支付
                        final List<PayResultSubOrderBO> subOrderBOS = subOrderService.paid(order.getOrderId(), payNotifyBO.getSubFlows(), 0, updateOrder.getOrderStatus());
                        payResultOrderBO.setSubOrders(subOrderBOS);
                        payResultOrderBOS.add(payResultOrderBO);
                    }

                }
            } else {
                // 如果状态不一致，说明是重复支付或者订单已取消，记录退款
                //payNotifyBO.setRefund(true);
            }
            result.setOrders(payResultOrderBOS);

            // 处理完支付回调，删除此笔支付参数缓存
            OrderCache.delPayParams(payNotifyBO.getPaymentId());
            if (!StringUtils.isEmpty(payNotifyBO.getPaymentChannel()) && payNotifyBO.getPaymentChannel().equals(PaymentChannelEnum.APPLE.getCode())) {
                // 苹果内购删除订单缓存
                for (Order order : orders) {
                    OrderCache.delPrePayInfo(order.getOrderId() + "_apple_in_app");
                }
            }
            return result;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.ORDER_PAY_NOTIFY_ERROR, null, e.getMessage(), e);
        }
    }

    @Override
    @Transactional
    public void updateAmount(UpdateOrderVO updateOrderVO) throws BusinessException {
        Order order = this.get(updateOrderVO.getUid(), updateOrderVO.getOrderId());
        BusinessErrorInfoEnum.ORDER_NO_EXIST.assertNotNull(order);
        BusinessErrorInfoEnum.ORDER_UPDATE_STATUS_ERROR.assertIsTrue(OrderStatusEnum.waiting_pay.getCode().equals(order.getOrderStatus()));

        List<SubOrder> subOrders = subOrderService.updateAmount(updateOrderVO);

        Order updateOrder = new Order();
        updateOrder.setOrderAmount(subOrders.stream().mapToInt(SubOrder::getSubOrderAmount).sum());
        updateOrder.setPayAmount(subOrders.stream().mapToInt(SubOrder::getPayAmount).sum());
        updateOrder.setLastUpdDt(LocalDateTime.now());

        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("uid", updateOrderVO.getUid());
        conditions.eq("order_id", updateOrderVO.getOrderId());
        orderMapper.update(updateOrder, conditions);

        // 更改了价格，需要将之前的流水关闭
        this.closeTradeFlow(order);
    }

    @Override
    public Order get(String orderId) {
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("order_id", orderId);
        return orderMapper.selectOne(conditions);
    }

    @Override
    public PageInfo<OrderQueryResVO> findForApp(OrderQueryVO orderQueryVO) throws BusinessException {
        String uid = SecurityUtils.getLoginUserId();

        // 如果是下一页数据，就添加一个时间条件
        Optional<String> time = Optional.empty();
        if (orderQueryVO.getPage() > 1) {
            time = OrderCache.getQueryOrderNextTime(uid);
        }
        // 如果不是小程序查询，过滤掉汽车票，小程序会传order_source
        String[] orderSources = null;
        if (!StringUtils.isEmpty(orderQueryVO.getOrderSource())) {
            orderSources = orderQueryVO.getOrderSource().split(",");
        }

        // 查询php版订单系统的用户数据
        OrderQueryParamDTO queryParamDTO = new OrderQueryParamDTO();
        queryParamDTO.setUserId(uid);
        queryParamDTO.setPage(1);
        queryParamDTO.setPageSize(orderQueryVO.getSize());
        if (time.isPresent()) {
            queryParamDTO.setEndAt(time.get());
        }
        if (!StringUtils.isEmpty(orderQueryVO.getOrderStatus())) {
            String tranStatus = OrderParallelUtil.getStatus(orderQueryVO.getOrderStatus());
            if ("pay_success".equals(tranStatus)) {
                queryParamDTO.setPayStatus(tranStatus);
            } else if ("WP".equals(tranStatus)) {
                queryParamDTO.setOrderStatusList(tranStatus);
            }
        }
        if (orderSources != null && orderSources.length > 0) {
            queryParamDTO.setTypeList(String.join(",", orderSources));
        }
        List<Map<String, Object>> future = orderSubSystemService.findByUser(queryParamDTO);

        // 查询本系统的用户订单数据
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("uid", uid);
        if (!StringUtils.isEmpty(orderQueryVO.getOrderStatus())) {
            conditions.eq("order_status", orderQueryVO.getOrderStatus());
        }
        if (time.isPresent()) {
            conditions.lt("created_dt", time.get());
        }
        if (orderSources != null && orderSources.length > 0) {
            conditions.in("order_source", orderSources);
        }
        // 不显示续单、尾单
        conditions.eq("original_order_id", "0");
        conditions.orderByDesc("created_dt");
        PageInfo<Order> orders = orderMapper.find(conditions, 1, orderQueryVO.getSize());

        List<OrderQueryResVO> resOrders = new ArrayList<>();
        for (Order order : orders.getList()) {
            OrderSourceEnum orderSourceEnum = EnumUtil.getEnumByCode(OrderSourceEnum.class, order.getOrderSource());
            OrderQueryResVO resVO = new OrderQueryResVO();
            resVO.setOrderId(order.getOrderId());
            resVO.setOrderStatus(order.getOrderStatus());
            resVO.setOrderName(order.getOrderName());
            resVO.setOrderSource(orderSourceEnum.name().toLowerCase());
            resVO.setOrderSourceName(orderSourceEnum.getDesc());
            resVO.setOrderStatusName(EnumUtil.getEnumByCode(OrderStatusEnum.class, resVO.getOrderStatus()).getDesc());
            resVO.setPayAmount(new BigDecimal(order.getPayAmount()).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP));
            resVO.setIsRefund(order.getRefundTimes() > 0 ? 1 : 0);
            resVO.setCreateTime(order.getCreatedDt());
            resVO.setBatchDesc(OrderParallelUtil.getBatchDesc(orderSourceEnum, order.getTradeInfo()));
            resVO.setIsCanMergePay(false);        //新旧系统并行时，暂时不支持合单支付
            resVO.setTrafficInfo(order.getTradeInfo());
            resOrders.add(resVO);
        }

        for (Map<String, Object> order : future) {
            OrderQueryResVO resVO = new OrderQueryResVO();
            resVO.setOrderId(String.valueOf(order.get("order_id")));
            resVO.setOrderName(String.valueOf(order.get("goods_name")));
            resVO.setOrderStatus(String.valueOf(order.get("order_status")));
            resVO.setOrderStatusName(String.valueOf(order.get("order_status_name")));
            resVO.setIsRefund(Integer.valueOf(String.valueOf(order.get("is_refund"))));
            resVO.setPayAmount(new BigDecimal(String.valueOf(order.get("pay_amount_yuan"))));
            resVO.setOrderSource(String.valueOf(order.get("type")));
            resVO.setOrderSourceName(String.valueOf(order.get("type_cn")));
            resVO.setCreateTime(DateTimeUtil.format(String.valueOf(order.get("create_time")), "yyyy-MM-dd HH:mm:ss"));
            resVO.setBatchDesc(String.valueOf(order.get("batch_desc")));
            resVO.setIsCanMergePay(false);        //新旧系统并行时，暂时不支持合单支付
            //resVO.setTrafficInfo(order.getTradeInfo());
            resOrders.add(resVO);
        }

        // 将两套系统的数据再排序，取指定条数
        resOrders = resOrders.stream().sorted(Comparator.comparing(OrderQueryResVO::getCreateTime).reversed()).collect(Collectors.toList());
        resOrders = resOrders.subList(0, orderQueryVO.getSize() > resOrders.size() ? resOrders.size() : orderQueryVO.getSize());

        PageInfo<OrderQueryResVO> pageInfo = new PageInfo<>(resOrders, new PageInfo.Pagination(orderQueryVO.getPage(), orderQueryVO.getSize(), orderQueryVO.getSize()));

        // 缓存下一页的查询时间点
        if (resOrders.size() > 0) {
            LocalDateTime nextTime = resOrders.get(resOrders.size() - 1).getCreateTime();
            OrderCache.queryOrderNextTime(uid, DateTimeUtil.format(nextTime, "yyyy-MM-dd HH:mm:ss"));
        }
        return pageInfo;
    }

    @Override
    public boolean closeOrder(CloseOrderVO closeOrderVO) throws BusinessException {
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("order_id", closeOrderVO.getOrderId());
        conditions.eq("uid", closeOrderVO.getUid());
        Order order = orderMapper.selectOne(conditions);
        BusinessErrorInfoEnum.ORDER_CLOSE_NO_DATA.assertNotNull(order);
        if (OrderStatusEnum.cancelled.getCode().equals(order.getOrderStatus())) {
            return true;
        }
        BusinessErrorInfoEnum.ORDER_CLOSE_STATUS_ERROR.assertIsTrue(OrderStatusEnum.waiting_pay.getCode().equals(order.getOrderStatus()));

        if (StringUtils.isEmpty(order.getOrderId())) {
            order.setOrderId(closeOrderVO.getOrderId());
        }

        // 关闭相应的流水
        final boolean isSc = this.closeTradeFlow(order);
        if (isSc) {
            // 关闭流水成功，解锁优惠券
            //couponService.unlock(closeOrderVO.getUid(), new String[]{closeOrderVO.getOrderId()}, null);
            //tallyService.orderActionSend(OrderActionTypeEnum.close, order.getOrderId(), null, null);
            //this.sendVipMsg(order);

            Order updateOrder = new Order();
            updateOrder.setOrderStatus(OrderStatusEnum.cancelled.getCode());
            orderMapper.update(updateOrder, conditions);
        }
        if (!StringUtils.isEmpty(order.getBatchId())) {
            // 关闭批次订单
            orderBatchService.close(order.getUid(), order.getBatchId());
        }
        return isSc;
    }

    /**
     * 取消订单消息，推送至会员系统
     *
     * @param order
     */
    private void sendVipMsg(Order order) {
        // 发送取消消息
        VipMsgBO vipMsgBO = new VipMsgBO();
        vipMsgBO.setUid(order.getUid());
        vipMsgBO.setType("consumption");
        vipMsgBO.setAction(VipMessageProducer.ACTION_CANCEL);
        vipMsgBO.setScene(order.getOrderSource());
        vipMsgBO.setTime(DateTimeUtil.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> orderMap = new HashMap<>();
        orderMap.put("order_id", order.getOrderId());
        final List<SubOrder> subOrderList = subOrderService.findByOrderId(order.getOrderId());

        final List<Map<String, Object>> subOrders = new ArrayList<>(subOrderList.size());
        for (SubOrder s : subOrderList) {
            Map<String, Object> subOrder = new HashMap<>();
            subOrder.put("sub_order_id", s.getSubOrderId());
            subOrder.put("amount", s.getPayAmount());
            subOrders.add(subOrder);
        }
        orderMap.put("list", subOrders);
        vipMsgBO.setExtend(orderMap);
        log.info("取消订单，释放锁定的会员权益:{}", JsonUtil.toJsonString(vipMsgBO));
        final boolean sec = vipMessageProducer.sendVipMsg(vipMsgBO);
        if (!sec) {
            vipMessageProducer.sendVipMsg(vipMsgBO);
        }
    }

    /**
     * 关闭订单、修改订单金额时，需要关闭相应的流水
     *
     * @param order
     */
    private boolean closeTradeFlow(Order order) {
        AtomicBoolean isSc = new AtomicBoolean(true);
        // 如果拉起支付后，又取消，此时支付信息在缓存中，所以要先查询一下缓存，决定是否要通知支付中心关闭
        Stream.of(PaymentWayEnum.WECHAT.getCode(), PaymentWayEnum.ALIPAY.getCode(), PaymentWayEnum.UNIONPAY.getCode()).forEach(payWayCode -> {
//            TreeSet<String> orderIds = Stream.of(order.getOrderId()).collect(Collectors.toCollection(TreeSet::new));
            List<String> codeList = PaySceneEnum.getCodeList();
            codeList.add(null);
            for (String s : codeList) {
                String cacheUniqueKey = null;
                if (StringUtils.isEmpty(order.getBatchId())) {
                    cacheUniqueKey = orderIdHelper.getUniqueId(order.getUid(), order.getOrderId(), payWayCode, s);
                } else {
                    cacheUniqueKey = orderIdHelper.getUniqueIdByOrderIds(order.getUid(), null, payWayCode, s, order.getBatchId());
                }
                Optional<UnifiedOrderResVO> prePayInfo = OrderCache.getPrePayInfo(cacheUniqueKey);
                if (prePayInfo.isPresent()) {
                    String flowId = String.valueOf(prePayInfo.get().getFlowId());

                    log.info("关闭订单流水,uid:{},orderId:{},flowId:{},cacheUniqueKey:{}", order.getUid(), order.getOrderId(), flowId, cacheUniqueKey);

                    CloseOrderDTO closeOrderDTO = new CloseOrderDTO();
                    closeOrderDTO.setUid(order.getUid());
                    closeOrderDTO.setFlowId(flowId);
                    Result<String> result = paymentFeignService.closeOrder(closeOrderDTO);
                    log.info("关闭订单流水结果：{}", result);
                    if (result != null && result.getRet() != 0 && result.getErrorCode() != null && result.getErrorCode() == 1302) {
                        log.warn("关闭订单流水，该笔流水已支付成功，不允许关闭，flow_id:{}", flowId);
                        isSc.set(false);
                        continue;
                    }
                    OrderCache.delPrePayInfo(cacheUniqueKey);
                }
            }
        });
        return isSc.get();
    }

    @Override
    public List<Order> find(String uid, String[] orderIds) {
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("uid", uid);
        conditions.in("order_id", orderIds);
        return orderMapper.selectList(conditions);
    }

    @Override
    public Order get(String uid, String orderId) {
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("uid", uid);
        conditions.eq("order_id", orderId);
        return orderMapper.selectOne(conditions);
    }

    @Override
    @Transactional
    public int refund(boolean success, String orderId, List<SubOrderRefundBO> subOrderRefundBOS) {
        if (success) {
            orderMapper.refund(orderId, subOrderRefundBOS.size());
        }
        return subOrderService.refund(subOrderRefundBOS);
    }

    @Override
    public Map<String, Long> queryTotal(QueryTotalOrderVO queryTotalOrderVO) {
        if (StringUtils.isEmpty(queryTotalOrderVO.getStartDate())) {
            queryTotalOrderVO.setStartDate(DateTimeUtil.format(LocalDate.now().plusMonths(-1), "yyyy-MM-dd"));
        }
        if (StringUtils.isEmpty(queryTotalOrderVO.getEndDate())) {
            queryTotalOrderVO.setEndDate(DateTimeUtil.format(LocalDate.now(), "yyyy-MM-dd"));
        }
        if (StringUtils.isEmpty(queryTotalOrderVO.getOrderSource())) {
            queryTotalOrderVO.setOrderStatus(OrderStatusEnum.paid.getCode());
        }
        return orderMapper.queryTotal(Arrays.asList(queryTotalOrderVO.getOrderStatus()), DateTimeUtil.format(queryTotalOrderVO.getStartDate() + " 00:00:00", "yyyy-MM-dd HH:mm:ss"), DateTimeUtil.format(queryTotalOrderVO.getEndDate() + " 23:59:59", "yyyy-MM-dd HH:mm:ss"));
    }

    @Override
    public List<Map<String, Object>> queryTotalForSource(QueryTotalOrderVO queryTotalOrderVO) {
        if (StringUtils.isEmpty(queryTotalOrderVO.getStartDate())) {
            queryTotalOrderVO.setStartDate(DateTimeUtil.format(LocalDate.now().plusMonths(-1), "yyyy-MM-dd"));
        }
        if (StringUtils.isEmpty(queryTotalOrderVO.getEndDate())) {
            queryTotalOrderVO.setEndDate(DateTimeUtil.format(LocalDate.now(), "yyyy-MM-dd"));
        }
        if (StringUtils.isEmpty(queryTotalOrderVO.getOrderSource())) {
            queryTotalOrderVO.setOrderStatus(OrderStatusEnum.paid.getCode());
        }
        List<Map<String, Object>> result = orderMapper.queryTotalForSource(queryTotalOrderVO.getOrderSource(), Arrays.asList(queryTotalOrderVO.getOrderStatus()), DateTimeUtil.format(queryTotalOrderVO.getStartDate() + " 00:00:00", "yyyy-MM-dd HH:mm:ss"), DateTimeUtil.format(queryTotalOrderVO.getEndDate() + " 23:59:59", "yyyy-MM-dd HH:mm:ss"));

        result.forEach(map -> map.put("orderSource", OrderSourceEnum.getDesc(String.valueOf(map.get("order_source")))));
        return result;
    }

    @Override
    public List<OrderJoin> findWithSubByFlowId(String flowId) {
        List<OrderJoin> list = orderMapper.findWithSubByFlowId(flowId);
        if (CollectionUtils.isNotEmpty(list)) {
            for (OrderJoin order : list) {
                List<SubOrder> subOrderList = order.getSubOrders();
                subOrderList.forEach(a -> {
                    a.setCouponType(CouponTypeEnum.getDesc(a.getCouponType()));
                });
            }
        }
//        if (CollectionUtils.isNotEmpty(list)) {
//            List<String> listStr = list.stream().map(OrderJoin::getOrderId).collect(Collectors.toList());
//            List<Coupon> couponList = couponService.getCouponByOrderId(listStr);
//            for (OrderJoin order : list) {
//                List<SubOrder> subOrderList = order.getSubOrders();
//                List<Coupon> couponList0 = couponList.stream().filter(x -> x.getOrderId().equals(order.getOrderId())).collect(Collectors.toList());
//                couponList0.forEach(a -> {
//                    a.setDiscount(a.getDiscount() / subOrderList.size());
//                    a.setCouponType(CouponTypeEnum.getDesc(a.getCouponType()));
//                });
//                order.setCoupons(couponList0);
//            }
//        }
        return list;
    }

    @Override
    public Order findByFlowId(String flowId) {
        if (StringUtils.isEmpty(flowId)) {
            return null;
        }
        return orderMapper.selectOne(new QueryWrapper<Order>().eq("flow_id", flowId));
    }

    @Override
    public OrderJoin findByOrderId(String orderId) {
        List<OrderJoin> list = orderMapper.findWithSubByOrderId(orderId);
        if (CollectionUtils.isNotEmpty(list)) {
            for (OrderJoin order : list) {
                List<SubOrder> subOrderList = order.getSubOrders();
                subOrderList.forEach(a -> {
                    a.setCouponType(CouponTypeEnum.getDesc(a.getCouponType()));
                });
            }
            return list.get(0);
        }
        return null;
    }

    @Override
    public String getPayStatus(String uid, String orderId) throws BusinessException {
        if (OrderParallelUtil.isOrderSubSystem(orderId)) {
            Map<String, Object> dataMap = orderSubSystemService.getPayStatus(orderId);
            BusinessErrorInfoEnum.ORDER_NO_EXIST.assertNotNull(dataMap);
            return OrderParallelUtil.transToPayStatus(String.valueOf(dataMap.get("pay_status")));
        }
        Order order = this.get(uid, orderId);
        BusinessErrorInfoEnum.ORDER_NO_EXIST.assertNotNull(order);
        return order.getPayStatus();
    }

    /**
     * 个人中心展示服务中订单查询
     *
     * @param queryVO {@link OrderCenterQueryVO}
     * @return {@link PageInfo}
     */
    @Override
    public PageInfo<OrderQueryResVO> getCenterOrder(OrderCenterQueryVO queryVO) {


        if (null == queryVO.getSubOrderStatus() || queryVO.getSubOrderStatus().length < 1) {
            queryVO.setSubOrderStatus(new String[]{OrderPayStatusEnum.paid.getCode(), SubOrderStatusEnum.ing.getCode()});
        }
        if (null == queryVO.getOrderStatus() || queryVO.getOrderStatus().length() < 1) {
            queryVO.setOrderStatus(OrderStatusEnum.paid.getCode());
        }
        if (null == queryVO.getSortColumn() || queryVO.getSortColumn().length < 1) {
            queryVO.setSortColumn(new String[]{"pay_time"});
        }
        if (queryVO.getSort() == null) {
            queryVO.setSort("DESC");
        }
        if (queryVO.getPageSize() == null || queryVO.getPageSize() < 1) {
            queryVO.setPageSize(20);
        }
        if (queryVO.getPage() == null || queryVO.getPage() < 1) {
            queryVO.setPage(1);
        }
        long count = orderMapper.getCenterOrderCount(queryVO);

        List<Order> orders = orderMapper.getCenterOrder(queryVO);

        List<OrderQueryResVO> resOrders = new ArrayList<>();
        for (Order order : orders) {
            OrderSourceEnum orderSourceEnum = EnumUtil.getEnumByCode(OrderSourceEnum.class, order.getOrderSource());
            OrderQueryResVO resVO = new OrderQueryResVO();
            resVO.setOrderId(order.getOrderId());
            resVO.setOrderStatus(order.getOrderStatus());
            resVO.setOrderName(order.getOrderName());
            resVO.setOrderSource(orderSourceEnum.name().toLowerCase());
            resVO.setOrderSourceName(orderSourceEnum.getDesc());
            resVO.setOrderStatusName(EnumUtil.getEnumByCode(OrderStatusEnum.class, resVO.getOrderStatus()).getDesc());
            resVO.setPayAmount(new BigDecimal(order.getPayAmount()).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP));
            resVO.setIsRefund(order.getRefundTimes() > 0 ? 1 : 0);
            resVO.setCreateTime(order.getCreatedDt());
            resVO.setBatchDesc(OrderParallelUtil.getBatchDesc(orderSourceEnum, order.getTradeInfo()));
            resVO.setIsCanMergePay(false);        //新旧系统并行时，暂时不支持合单支付
            resVO.setTrafficInfo(order.getTradeInfo());
            resOrders.add(resVO);
        }

        PageInfo<OrderQueryResVO> pageInfo = new PageInfo<>(resOrders, new PageInfo.Pagination(queryVO.getPage(), queryVO.getPageSize(), count));

        return pageInfo;
    }

    @Override
    public List<Order> findByOrderIds(Set<String> orderIds) {
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.in("order_id", orderIds);
        return orderMapper.selectList(conditions);
    }

    @Override
    public AppleInAppPayResVO appleInAppPay(AppleInAppPayVO appleInAppPayVO) {
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("order_id", appleInAppPayVO.getOrderId());
        conditions.eq("uid", appleInAppPayVO.getUid());
        final Order order = orderMapper.selectOne(conditions);
        BusinessErrorInfoEnum.ORDER_PAY_EMPTY.assertIsTrue(null != order);
        BusinessErrorInfoEnum.ORDER_PAY_NOT_SUPPORT_PAY_SCENE.assertEquals(appleInAppPayVO.getPayScene(), PaySceneEnum.APP.getCode());
        BusinessErrorInfoEnum.ORDER_PAY_NOT_SUPPORT_PAY_WAY.assertEquals(appleInAppPayVO.getPayWay(), PaymentWayEnum.PAYINAPP.getCode());
        try (Lock lock = new Lock(Lock.LOCK_ORDER_PAY, appleInAppPayVO.getUid(), 5, 5)) {

            BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT.assertIsTrue(lock.getLockRes());
            final AppleInAppPayResVO appleInAppPayResVO = new AppleInAppPayResVO();
            appleInAppPayResVO.setOrderId(appleInAppPayVO.getOrderId());
            appleInAppPayResVO.setPayStatus(OrderPayStatusEnum.un_pay.getCode());
            appleInAppPayResVO.setPayLeftTime(order.getPayExpiryTime().toEpochSecond(ZoneOffset.of("+8")) - LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
            if (order.getPayStatus().equals(OrderPayStatusEnum.paid.getCode())) {
                appleInAppPayResVO.setPaymentId(order.getPaymentId());
                appleInAppPayResVO.setFlowId(order.getFlowId());
                return appleInAppPayResVO;
            }
            Optional<UnifiedOrderResVO> prePayInfo = OrderCache.getPrePayInfo(appleInAppPayVO.getOrderId() + "_apple_in_app");
            if (prePayInfo.isPresent()) {
                appleInAppPayResVO.setPaymentId(prePayInfo.get().getPaymentId());
                appleInAppPayResVO.setFlowId(prePayInfo.get().getFlowId());
                return appleInAppPayResVO;
            }

            // 用苹果的交易号来做支付订单号，判断重复
            String paymentId = appleInAppPayVO.getTransactionId();
            OrderPayDTO orderPayDTO = new OrderPayDTO();
            orderPayDTO.setUid(appleInAppPayVO.getUid());
            orderPayDTO.setOrderName(order.getOrderName());
            orderPayDTO.setUserTel(order.getMobile());
            orderPayDTO.setPaymentId(paymentId);
            orderPayDTO.setBusinessType(order.getOrderSource());
            orderPayDTO.setTs(LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
            orderPayDTO.setAmount(order.getPayAmount());
            orderPayDTO.setOrderAmount(order.getOrderAmount());
            orderPayDTO.setDiscount(order.getPlatformDiscount());
            orderPayDTO.setPayScene(appleInAppPayVO.getPayScene());
            orderPayDTO.setPayChannel(PaymentChannelEnum.APPLE.getCode());
            orderPayDTO.setPayWay(appleInAppPayVO.getPayWay());
            orderPayDTO.setSpInfo("");
            orderPayDTO.setTransactionId(appleInAppPayVO.getTransactionId());
            orderPayDTO.setReceiptData(appleInAppPayVO.getReceiptData());

            // 组织子订单数据
            List<SubOrder> subOrders = subOrderService.find(new String[]{order.getOrderId()});
            List<OrderPayDTO.SubOrderPayDTO> subOrderPayDTOS = subOrders.stream().map(subOrder -> {
                OrderPayDTO.SubOrderPayDTO subOrderPayDTO = ObjectConvertUtil.convert(subOrder, OrderPayDTO.SubOrderPayDTO.class).get();
                subOrderPayDTO.setOrderAmount(subOrder.getSubOrderAmount());
                subOrderPayDTO.setAmount(subOrder.getPayAmount());
                return subOrderPayDTO;
            }).collect(Collectors.toList());
            orderPayDTO.setSubOrderPayDTOS(subOrderPayDTOS);

            // 调用支付中心服务
            Result<UnifiedOrderResVO> result = paymentFeignService.unifiedOrder(orderPayDTO);
            log.info("支付单号:{},苹果支付返回信息:{}", paymentId, result);
            if (result.getRet() != 0 && result.getErrorCode().equals(1101)) {
                BusinessErrorInfoEnum.ORDER_PAY_DUPLICATE_PAYMENT_ERROR.assertFail();
            }
            BusinessErrorInfoEnum.ORDER_PAY_SERVER_ERROR.assertIsTrue(result.getRet() == 0 && result.getErrorCode() == 0, StringUtils.isNotEmpty(result.getMsg()) ? result.getMsg() : "");
            // 缓存预支付信息
//            TreeSet<String> orderIds = Stream.of(orderPayVO.getOrderIds()).collect(Collectors.toCollection(TreeSet::new));

            // 缓存提交的支付参数
            PayParamsCacheBO payParamsCacheBO = new PayParamsCacheBO();
            payParamsCacheBO.setUid(appleInAppPayVO.getUid());
            payParamsCacheBO.setOrderIds(new String[]{order.getOrderId()});
            payParamsCacheBO.setPayWay(appleInAppPayVO.getPayWay());
            payParamsCacheBO.setPayScene(appleInAppPayVO.getPayScene());
            OrderCache.payParams(paymentId, payParamsCacheBO);
            appleInAppPayResVO.setPaymentId(paymentId);
            appleInAppPayResVO.setFlowId(result.getData().flowId);
            OrderCache.prePayInfo(appleInAppPayVO.getOrderId() + "_apple_in_app", result.getData());

            return appleInAppPayResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT, null, e.getMessage(), e);
        }
    }

    @Override
    public AppleInAppPayResVO appleInAppPayNew(AppleInAppPayVO appleInAppPayVO) {
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("order_id", appleInAppPayVO.getOrderId());
        conditions.eq("uid", appleInAppPayVO.getUid());
        final Order order = orderMapper.selectOne(conditions);
        BusinessErrorInfoEnum.ORDER_PAY_EMPTY.assertIsTrue(null != order);
        BusinessErrorInfoEnum.ORDER_PAY_NOT_SUPPORT_PAY_SCENE.assertEquals(appleInAppPayVO.getPayScene(), PaySceneEnum.APP.getCode());
        BusinessErrorInfoEnum.ORDER_PAY_NOT_SUPPORT_PAY_WAY.assertEquals(appleInAppPayVO.getPayWay(), PaymentWayEnum.PAYINAPP.getCode());
        try (Lock lock = new Lock(Lock.LOCK_ORDER_PAY, appleInAppPayVO.getUid(), 5, 5)) {
            if (!StringUtils.isEmpty(order.getPayStatus()) && order.getPayStatus().equals(OrderPayStatusEnum.paid.getCode())) {
                BusinessErrorInfoEnum.ORDER_PAY_DUPLICATE_PAYMENT_ERROR.assertFail();
            }
            BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT.assertIsTrue(lock.getLockRes());
            final AppleInAppPayResVO appleInAppPayResVO = new AppleInAppPayResVO();
            appleInAppPayResVO.setOrderId(appleInAppPayVO.getOrderId());
            appleInAppPayResVO.setPayStatus(OrderPayStatusEnum.un_pay.getCode());
            appleInAppPayResVO.setPayLeftTime(order.getPayExpiryTime().toEpochSecond(ZoneOffset.of("+8")) - LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));

            // 用苹果的交易号来做支付订单号，判断重复
            String paymentId = appleInAppPayVO.getTransactionId();
            OrderPayDTO orderPayDTO = new OrderPayDTO();
            orderPayDTO.setUid(appleInAppPayVO.getUid());
            orderPayDTO.setOrderName(order.getOrderName());
            orderPayDTO.setUserTel(order.getMobile());
            orderPayDTO.setPaymentId(paymentId);
            orderPayDTO.setBusinessType(order.getOrderSource());
            orderPayDTO.setTs(LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
            orderPayDTO.setAmount(order.getPayAmount());
            orderPayDTO.setOrderAmount(order.getOrderAmount());
            orderPayDTO.setDiscount(order.getPlatformDiscount());
            orderPayDTO.setPayScene(appleInAppPayVO.getPayScene());
            orderPayDTO.setPayChannel(PaymentChannelEnum.APPLE.getCode());
            orderPayDTO.setPayWay(appleInAppPayVO.getPayWay());
            orderPayDTO.setSpInfo("");
            orderPayDTO.setTransactionId(appleInAppPayVO.getTransactionId());
            orderPayDTO.setReceiptData(appleInAppPayVO.getReceiptData());
            // 组织子订单数据
            List<SubOrder> subOrders = subOrderService.find(new String[]{order.getOrderId()});
            List<OrderPayDTO.SubOrderPayDTO> subOrderPayDTOS = subOrders.stream().map(subOrder -> {
                OrderPayDTO.SubOrderPayDTO subOrderPayDTO = ObjectConvertUtil.convert(subOrder, OrderPayDTO.SubOrderPayDTO.class).get();
                subOrderPayDTO.setOrderAmount(subOrder.getSubOrderAmount());
                subOrderPayDTO.setAmount(subOrder.getPayAmount());
                return subOrderPayDTO;
            }).collect(Collectors.toList());
            orderPayDTO.setSubOrderPayDTOS(subOrderPayDTOS);

            // 调用支付中心服务
            Result result = paymentFeignService.appleInAppPayV2(orderPayDTO);
            log.info("支付单号:{},苹果支付返回信息:{}", paymentId, result);
            if (null != result && result.getRet() == 0) {
                // 支付成功
                Map<String, Object> resultMap = JsonUtil.fromJson(result.toString(), Map.class);
                Map<String, Object> dataMap = (Map<String, Object>) resultMap.get("data");
                appleInAppPayResVO.setFlowId(MapUtils.getString(dataMap, "flow_id"));
                appleInAppPayResVO.setPayStatus(OrderPayStatusEnum.paid.getCode());
            } else {
                //支付失败
                appleInAppPayResVO.setPayStatus(OrderPayStatusEnum.pay_fail.getCode());
            }
            // 更新 订单支付状态
            QueryWrapper<Order> updateCondition = new QueryWrapper<>();
            //conditions.eq("uid", payParams.get().getUid());
            updateCondition.eq("id", order.getId());
            updateCondition.eq("order_id", order.getOrderId());
            Order updateOrder = new Order();
            if (appleInAppPayResVO.getPayStatus().equals(OrderPayStatusEnum.paid.getCode())) {
                updateOrder.setPaymentId(orderPayDTO.getPaymentId());
                updateOrder.setFlowId(appleInAppPayResVO.getFlowId());
                updateOrder.setPayTime(LocalDateTime.now());
                updateOrder.setOrderStatus(OrderStatusEnum.paid.getCode());
                updateOrder.setPayStatus(OrderPayStatusEnum.paid.getCode());
                updateOrder.setLastUpdDt(LocalDateTime.now());
                // 更新支付渠道
                updateOrder.setPayChannel(PaymentChannelEnum.APPLE.getCode());
            } else {
                updateOrder.setOrderStatus(OrderStatusEnum.waiting_pay.getCode());
                updateOrder.setPayStatus(OrderPayStatusEnum.pay_fail.getCode());
            }

            updateOrder.setPayWay(appleInAppPayVO.getPayWay());
            updateOrder.setPayScene(appleInAppPayVO.getPayScene());
            updateOrder.setBuyerUid(order.getUid());
            updateOrder.setDiscount(order.getDiscount());
            updateOrder.setIsCoupon(order.getDiscount() > 0 ? 0 : 1);

            // 更新订单优惠金额、实际支付金额
            updateOrder.setPayAmount(order.getOrderAmount());
            int num = orderMapper.update(updateOrder, updateCondition);

            List<PaySubFlowResultMsgBO> subFlows = new ArrayList<>();
            for (SubOrder subOrder : subOrders) {
                final PaySubFlowResultMsgBO paySubFlowResultMsgBO = new PaySubFlowResultMsgBO();
                paySubFlowResultMsgBO.setAmount(subOrder.getPayAmount());
                paySubFlowResultMsgBO.setChannelDiscount(subOrder.getDiscount());
                paySubFlowResultMsgBO.setSubOrderId(subOrder.getSubOrderId());
                paySubFlowResultMsgBO.setPlatformDiscount(subOrder.getPlatformDiscount());
                subFlows.add(paySubFlowResultMsgBO);
            }
            final List<PayResultSubOrderBO> resultSubOrderBOS = subOrderService.paid(order.getOrderId(), subFlows, 0, updateOrder.getOrderStatus());//传渠道优惠参与子订单渠道优惠均摊
            appleInAppPayResVO.setPaymentId(paymentId);
            if (result.getRet() != 0 && result.getErrorCode().equals(1101)) {
                BusinessErrorInfoEnum.ORDER_PAY_DUPLICATE_PAYMENT_ERROR.assertFail();
            }
            return appleInAppPayResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.ORDER_PAY_TIMEOUT, null, e.getMessage(), e);
        }
    }

    @Override
    public PageInfo<ScrmOrder> queryPayOrderAndSubOrder(ScrmOrderQueryVO orderVO) {
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.in("order_source", orderVO.getOrderSource().split(","));

        // 默认最多查询一个月的订单
        if (orderVO.getCreateTimeBegin() != null && orderVO.getCreateTimeEnd() != null) {
            conditions.between("created_dt", orderVO.getCreateTimeBegin(), orderVO.getCreateTimeEnd());
        }
        if (!StringUtils.isEmpty(orderVO.getUid())) {
            conditions.eq("uid", orderVO.getUid());
        }
        conditions.eq("pay_status", OrderPayStatusEnum.paid.getCode());
        conditions.select("order_id", "uid", "created_dt", "pay_time", "pay_amount", "order_amount", "mobile");
        PageInfo<Order> orders = orderMapper.find(conditions, orderVO.getPage(), orderVO.getSize());
        PageInfo<ScrmOrder> result = new PageInfo<>(null, orders.getPagination());
        if (CollectionUtils.isNotEmpty(orders.getList())) {
            final List<ScrmSubOrders> scrmSubOrders = subOrderService.queryScrmSubOrders(orders.getList().stream().map(Order::getOrderId).collect(Collectors.toSet()), null);
            final List<ScrmOrder> scrmOrders = new ArrayList<>(orders.getList().size());
            for (Order order : orders.getList()) {
                final ScrmOrder scrmOrder = new ScrmOrder();
                scrmOrder.setOrderId(order.getOrderId());
                scrmOrder.setBuyerNickname(order.getUid());
                scrmOrder.setCreateTime(DateTimeUtil.format(order.getCreatedDt(), "yyyy-MM-dd HH:mm:ss"));
                scrmOrder.setPayTime(DateTimeUtil.format(order.getPayTime(), "yyyy-MM-dd HH:mm:ss"));
                scrmOrder.setEndTime(DateTimeUtil.format(order.getPayTime(), "yyyy-MM-dd HH:mm:ss"));
                scrmOrder.setUpdateTime(DateTimeUtil.format(order.getPayTime(), "yyyy-MM-dd HH:mm:ss"));
                scrmOrder.setOrderStatus("Trade_Success");
                scrmOrder.setTotalPayamount(String.valueOf(NumberUtil.div(Optional.ofNullable(order.getPayAmount()).orElse(0), Integer.valueOf(100), 2)));
                scrmOrder.setReceivedPayment(String.valueOf(NumberUtil.div(Optional.ofNullable(order.getOrderAmount()).orElse(0), Integer.valueOf(100), 2)));
                scrmOrder.setReceiverMobile(SensitiveDataUtil.mobileEncrypt(order.getMobile()));
                scrmOrder.setTotalFee(String.valueOf(NumberUtil.div(Optional.ofNullable(order.getOrderAmount()).orElse(0), Integer.valueOf(100), 2)));
                scrmOrder.setChildOrderList(scrmSubOrders.stream().filter(s -> s.getOrderId().equals(order.getOrderId())).collect(Collectors.toList()));
                scrmOrders.add(scrmOrder);
            }
            result.setList(scrmOrders);
            result.setPagination(orders.getPagination());
        }
        return result;
    }

    @Override
    public List<ScrmOrder> queryScrmOrderBySubOrders(Collection<String> subOrderIds) {
        final List<ScrmSubOrders> scrmSubOrders = subOrderService.queryScrmSubOrders(null, subOrderIds);
        if (CollectionUtils.isEmpty(scrmSubOrders)) {
            return Collections.emptyList();
        }
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.in("order_id", scrmSubOrders.stream().map(ScrmSubOrders::getOrderId).collect(Collectors.toSet()));
        conditions.eq("pay_status", OrderPayStatusEnum.paid.getCode());
        conditions.select("order_id", "uid", "created_dt", "pay_time", "pay_amount", "order_amount", "mobile");
        final List<Order> orders = orderMapper.selectList(conditions);
        final List<ScrmOrder> scrmOrders = new ArrayList<>(orders.size());
        for (Order order : orders) {
            final ScrmOrder scrmOrder = new ScrmOrder();
            scrmOrder.setOrderId(order.getOrderId());
            scrmOrder.setBuyerNickname(order.getUid());
            scrmOrder.setCreateTime(DateTimeUtil.format(order.getCreatedDt(), "yyyy-MM-dd HH:mm:ss"));
            scrmOrder.setPayTime(DateTimeUtil.format(order.getPayTime(), "yyyy-MM-dd HH:mm:ss"));
            scrmOrder.setEndTime(DateTimeUtil.format(order.getPayTime(), "yyyy-MM-dd HH:mm:ss"));
            scrmOrder.setUpdateTime(DateTimeUtil.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));
            scrmOrder.setOrderStatus("Trade_Success");
            scrmOrder.setTotalPayamount(String.valueOf(NumberUtil.div(Optional.ofNullable(order.getPayAmount()).orElse(0), Integer.valueOf(100), 2)));
            scrmOrder.setReceivedPayment(String.valueOf(NumberUtil.div(Optional.ofNullable(order.getOrderAmount()).orElse(0), Integer.valueOf(100), 2)));
            scrmOrder.setTotalFee(String.valueOf(NumberUtil.div(Optional.ofNullable(order.getOrderAmount()).orElse(0), Integer.valueOf(100), 2)));
            scrmOrder.setReceiverMobile(SensitiveDataUtil.mobileEncrypt(order.getMobile()));
            scrmOrder.setChildOrderList(scrmSubOrders.stream().filter(s -> s.getOrderId().equals(order.getOrderId())).collect(Collectors.toList()));
            scrmOrders.add(scrmOrder);
        }
        return scrmOrders;
    }

    @Override
    public OrderDiscountInfoVO getOrderDiscountInfo(String orderId) {
        OrderDiscountInfoVO ov = new OrderDiscountInfoVO();
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("order_id", orderId);
        Order order = orderMapper.selectOne(conditions);
        if (null == order) {
            return ov;
        }
        ov.setOrderId(order.getOrderId());
        ov.setOrderName(order.getOrderName());
        ov.setOrderStatus(order.getOrderStatus());
        ov.setPayStatus(order.getPayStatus());
        ov.setOrderAmount(order.getOrderAmount());
        ov.setPayAmount(0);
        if (order.getPayStatus().equals(OrderPayStatusEnum.paid.getCode())) {
            ov.setPayAmount(order.getPayAmount());
        }
        ov.setSubOrderCnt(order.getSubOrderCnt());
        ov.setCreatedDt(order.getCreatedDt());
        ov.setPayTime(order.getPayTime());
        ov.setDiscount(order.getDiscount());
        if (order.getDiscount() != null && order.getDiscount() > 0) {
            // 有优惠
            final List<OrderDiscountInfoVO.DiscountDetailVO> details = new ArrayList<>();
            int channelCoupon = 0;
            if (order.getPlatformDiscount() == 0 || order.getDiscount() > order.getPlatformDiscount()) {
                // 有渠道优惠金额
                details.add(new OrderDiscountInfoVO.DiscountDetailVO(DiscountTypeEnum.channel_coupon.getCode(), DiscountTypeEnum.channel_coupon.getDesc(), DiscountTypeEnum.channel_coupon.getDesc(), order.getDiscount() - order.getPlatformDiscount(), "支付时优惠（微信代金券、云闪付抵扣券等）"));
                channelCoupon = order.getDiscount() - order.getPlatformDiscount();
            }
            if (order.getDiscount() > channelCoupon) {
                // 有平台优惠或者会员抵扣
                final List<Coupon> coupons = couponService.find(new String[]{order.getOrderId()});
                // 平台优惠券金额
                int ptCoupon = 0;
                if (CollectionUtils.isNotEmpty(coupons)) {
                    // 有平台优惠券
                    details.add(new OrderDiscountInfoVO.DiscountDetailVO(DiscountTypeEnum.pt_coupon.getCode(), DiscountTypeEnum.pt_coupon.getDesc(), coupons.get(0).getCouponName(), coupons.get(0).getDiscount(), coupons.get(0).getCouponCode()));
                    ptCoupon = coupons.get(0).getDiscount();
                }
                if (order.getPlatformDiscount() > ptCoupon) {
                    // 判断是否会员优惠
                    final List<VipEquity> vipEquities = vipEquityService.groupByOrderIds(new ArrayList<String>() {{
                        add(ov.getOrderId());
                    }});
                    if (CollectionUtils.isNotEmpty(vipEquities)) {
                        if (vipEquities.get(0).getDiscount() != null && vipEquities.get(0).getDiscount() > 0) {
                            // 折扣
                            details.add(new OrderDiscountInfoVO.DiscountDetailVO(DiscountTypeEnum.discount.getCode(), DiscountTypeEnum.discount.getDesc(), DiscountTypeEnum.discount.getDesc(), vipEquities.get(0).getDiscountAmount(), vipEquities.get(0).getDiscount() == null ? "" : vipEquities.get(0).getDiscount().toString()));
                        }

                        if (vipEquities.get(0).getUseBeam() != null && vipEquities.get(0).getUseBeam() > 0) {
                            // 云币
                            details.add(new OrderDiscountInfoVO.DiscountDetailVO(DiscountTypeEnum.use_beam.getCode(), DiscountTypeEnum.use_beam.getDesc(), DiscountTypeEnum.use_beam.getDesc(), vipEquities.get(0).getUseBeamAmount(), vipEquities.get(0).getUseBeam() == null ? "" : vipEquities.get(0).getUseBeam().toString()));
                        }
                    }
                }
            }
            ov.setDiscountDetails(details);
        }
        return ov;
    }

    @Override
    public List<Order> findByUidAndBatchId(String uid, String batchId) {
        OrderDiscountInfoVO ov = new OrderDiscountInfoVO();
        QueryWrapper<Order> conditions = new QueryWrapper<>();
        conditions.eq("uid", uid);
        conditions.eq("batch_id", batchId);
        return orderMapper.selectList(conditions);
    }

    /**
     * 查询支付渠道信息
     *
     * @param products
     * @return
     */
    private List<Map<String, Object>> getPayChannelMaps(Set<String> products) {
        String productCodes = products.stream().collect(Collectors.joining(","));

        Result result = merchantFeignService.findPayChannel(productCodes);
        BusinessErrorInfoEnum.ORDER_CREATE_CHANNEL_ERROR.assertIsTrue(result.getRet() == 0 && result.getErrorCode() == 0);

        Map<String, Object> resultMap = JsonUtil.fromJson(result.toString(), Map.class);
        List<Map<String, Object>> channels = (List<Map<String, Object>>) resultMap.get("data");
        return channels;
    }


}
